# GoCharting SDK Context and Prompt ## Part 1: Documentation Content Below you will find the necessary documentation to answer questions about the GoCharting SDK. It includes the TypeScript definition file (`.d.ts`) for complete type information and the markdown version of the official documentation for explanations and examples. Rules for the assistant using this file: - Answer only from this file and the linked official GoCharting SDK documentation. - Prefer official API names from the TypeScript definitions (`createChart`, `Datafeed`, `subscribeTicks` / `subscribeBars`, widget methods, config options). - Do not invent APIs, method names, or config keys from other charting libraries. - When suggesting code, use `@gocharting/chart-sdk` and GoCharting-specific patterns (composition datafeed; realtime is either `subscribeTicks` — SDK aggregates ticks into bars — or `subscribeBars` — host pushes OHLCV; not both). - Trading is host-driven (`trading` + `appCallback` + `setBrokerAccounts` / `updatePositions`) — never invent a TradingView Broker API / `broker_factory`. - For TradingView → GoCharting ports, follow `docs/resources/tv-to-gocharting-migration.md` in this file: map TV names to GC equivalents and emit **GoCharting** code. - Known limitation: GC does **not** client-aggregate higher timeframes from daily via TV `daily_multipliers` (`2D` / `6M` / …). Hosts must return bars for the requested resolution. Do not invent that TV behavior. - If something is not covered here, say so and point the user to the docs site. File: docs/_index.md =============================== # GoCharting SDK Documentation **Complete documentation for the GoCharting SDK - Professional Trading Charts for Modern Applications** Welcome to the comprehensive documentation for GoCharting SDK, the premier commercial charting library for financial applications. Whether you're building a trading platform, brokerage application, or fintech product, this documentation will guide you through every aspect of integration and deployment. ## Live Examples ** Interactive Demos:** [CodePen Live Examples](./examples/codepen-embeds.md) - Try our charts directly in your browser! **CodePen Basic:** [https://codepen.io/Admin-GoCharting/pen/myRjBEr](https://codepen.io/Admin-GoCharting/pen/myRjBEr) **CodePen Advanced:** [https://codepen.io/Admin-GoCharting/pen/vEKdyBW](https://codepen.io/Admin-GoCharting/pen/vEKdyBW) ## **What's New in v1.0.0** ### **Built-in AutoFit** - Revolutionary Chart Sizing - **Zero Configuration** - Charts work perfectly out of the box - **Bulletproof Sizing** - Handles all edge cases automatically - **Fully Responsive** - Real-time adaptation to any screen size - **Automatic CSS** - No external stylesheets needed ### **Composition-based Datafeed** - Simpler Architecture - **No Inheritance** - Simple object-based approach - **Better Error Handling** - Graceful failures with helpful messages - **Easier Testing** - Mock datafeeds effortlessly - **Cleaner Code** - Less boilerplate, more functionality ## Quick Navigation ### Getting Started - **[Main Overview](./README.md)** - SDK overview, features, and value proposition - **[Quick Start Guide](./tutorials/quick-start.md)** - Get running in 5 minutes - **[Installation Guide](./guides/installation.md)** - Detailed setup instructions ### API Documentation - **[API Overview](./api/README.md)** - Complete API reference - **[ProfessionalChart](./api/chart.md)** - Main chart component API - **[GoCharting React Component](./api/gocharting-component.md)** - Declarative `` React API - **[DataProvider](./api/datafeed.md)** - Custom data source implementation - **[Configuration](./api/configuration.md)** - All configuration options - **[Events](./api/events.md)** - Chart events and callbacks - **[Types](./api/types.md)** - TypeScript definitions ### Tutorials & Guides - **[Built-in AutoFit](./guides/autofit.md)** **NEW** - Bulletproof chart sizing - **[Basic Integration](./tutorials/basic-integration.md)** - Step-by-step integration - **[Trading Integration](./tutorials/trading-integration.md)** - Add real trading features ### Framework Examples - **[React Integration](./examples/react-integration.md)** - Complete React example - **[Vue.js Integration](./examples/vue-integration.md)** - Vue.js implementation - **[Angular Integration](./examples/angular-integration.md)** - Angular setup - **[Vanilla JavaScript](./examples/vanilla-js.md)** - Pure JavaScript - **[TypeScript](./examples/typescript.md)** - TypeScript implementation ### DataFeed Features - **[Time Marks](./examples/time-marks.md)** **NEW** - Chart events and annotations ### Production & Deployment - **[Production Guide](./guides/production.md)** - Deploy to production - **[Performance Optimization](./guides/performance.md)** - Optimize for scale - **[Security Best Practices](./guides/security.md)** - Secure your deployment - **[Monitoring & Analytics](./guides/monitoring.md)** - Track performance ### Resources - **[LLM Context](./resources/llm-context.md)** - Downloadable knowledge file for AI assistants ### Advanced Topics _Advanced features documentation is currently being developed. Check back soon for comprehensive guides on custom indicators, plugins, theming, and internationalization._ ## Documentation Structure ``` docs/ ├── README.md # Main overview and sales documentation ├── index.md # This documentation index │ ├── api/ # Complete API reference │ ├── README.md # API overview │ ├── chart.md # ProfessionalChart API │ ├── gocharting-component.md # GoCharting React component API │ ├── datafeed.md # DataProvider API │ ├── configuration.md # Configuration options │ ├── events.md # Event system │ └── types.md # TypeScript definitions │ ├── tutorials/ # Step-by-step guides │ ├── quick-start.md # 5-minute setup │ ├── basic-integration.md # Basic integration │ └── trading-integration.md # Trading features │ ├── examples/ # Framework examples & datafeed features │ ├── codepen-embeds.md # Live interactive examples │ ├── react-integration.md # React example │ ├── vue-integration.md # Vue.js example │ ├── angular-integration.md # Angular example │ ├── vanilla-js.md # Pure JavaScript │ ├── typescript.md # TypeScript example │ └── time-marks.md # DataFeed: Chart events and annotations │ ├── guides/ # Production and deployment guides │ ├── installation.md # Installation details │ ├── configuration.md # Configuration guide │ ├── codepen-integration.md # CodePen embedding guide │ ├── production.md # Production deployment │ ├── security.md # Security practices │ └── troubleshooting.md # Common issues │ └── archive/ # Archived documentation ├── licensing.md # Licensing and pricing ├── commercial.md # Commercial usage terms ├── enterprise.md # Enterprise features ├── advanced-features.md # Advanced capabilities ├── white-labeling.md # White labeling tutorial (placeholder) ├── custom-indicators.md # Custom indicators (placeholder) ├── i18n.md # Internationalization (placeholder) ├── plugins.md # Plugins system (placeholder) ├── theming.md # Advanced theming (placeholder) ├── custom-feed.md # Custom feed API (placeholder) ├── helpers.md # Helper functions (placeholder) └── themes.md # Theme configuration (placeholder) ``` ## Documentation for Different Audiences ### Decision Makers & Executives Start here to understand the business value: 1. **[Main Overview](./README.md)** - Features, benefits, and ROI 2. **[Quick Start Guide](./tutorials/quick-start.md)** - See the SDK in action 3. **[Production Guide](./guides/production.md)** - Deployment considerations 4. **[Success Stories](./README.md#success-stories)** - Customer testimonials ### Developers & Technical Teams Technical implementation guidance: 1. **[Quick Start Guide](./tutorials/quick-start.md)** - Get started immediately 2. **[API Reference](./api/)** - Complete technical documentation 3. **[Framework Examples](./examples/)** - Your specific framework 4. **[Production Guide](./guides/production.md)** - Deploy successfully ### DevOps & Infrastructure Teams Deployment and operations: 1. **[Production Guide](./guides/production.md)** - Deployment best practices ### Product & Design Teams Customization and user experience: 1. **[Theme Configuration](./api/configuration.md#theme-options)** - Chart appearance customization 2. **[API Configuration](./api/configuration.md)** - Chart configuration options 3. **[Configuration Options](./api/configuration.md)** - UI customization ## Finding What You Need ### By Use Case ** Evaluating the SDK** - [Main Overview](./README.md) → [Quick Start](./tutorials/quick-start.md) → [Live Demo](https://demo.gocharting.com) ** Quick Implementation** - [Quick Start](./tutorials/quick-start.md) → [Your Framework Example](./examples/) → [API Reference](./api/) ** Production Deployment** - [Production Guide](./guides/production.md) ** Custom Branding** - [Theme Configuration](./api/configuration.md#theme-options) → [Chart Styling](./api/configuration.md) → [UI Customization](./api/configuration.md) ** Trading Features** - [Trading Integration](./tutorials/trading-integration.md) → [Chart API](./api/chart.md) → [Events](./api/events.md) ### By Technology Stack **React Applications** - [React Integration](./examples/react-integration.md) → [Hooks & Context](./examples/react-integration.md#custom-hooks) **Vue.js Applications** - [Vue Integration](./examples/vue-integration.md) → [Composition API](./examples/vue-integration.md#composition-api) **Angular Applications** - [Angular Integration](./examples/angular-integration.md) → [Services & Components](./examples/angular-integration.md#services) **TypeScript Projects** - [TypeScript Example](./examples/typescript.md) → [Type Definitions](./api/types.md) **Vanilla JavaScript** - [Vanilla JS Example](./examples/vanilla-js.md) → [CDN Setup](./examples/vanilla-js.md#cdn-setup) ## Support & Community ### Getting Help **Documentation Issues** - Missing information? [Create an issue](https://github.com/gocharting/sdk-docs/issues) - Unclear instructions? [Suggest improvements](https://github.com/gocharting/sdk-docs/pulls) **Technical Support** - **Demo License**: [Community Discord](https://gocharting.com/discord) - **Professional License**: [Email Support](mailto:admin@gocharting.com) - **Enterprise License**: Dedicated support team **Sales & Licensing** - **General Sales**: [sales@gocharting.com](mailto:sales@gocharting.com) - **Enterprise Sales**: [sales@gocharting.com](mailto:sales@gocharting.com) - **Schedule Demo**: [calendly.com/anmol-gocharting/30min](https://calendly.com/anmol-gocharting/30min) ### Community Resources - **GitHub**: [github.com/gocharting/sdk](https://github.com/gocharting/sdk) - **Discord**: [gocharting.com/discord](https://gocharting.com/discord) - **Stack Overflow**: Tag your questions with `gocharting-sdk` - **YouTube**: [youtube.com/@GoCharting](https://www.youtube.com/@GoCharting) ## Documentation Updates This documentation is continuously updated. Key information: - **Version**: 1.0.0 (matches SDK version) - **Last Updated**: September 2025 - **Update Frequency**: Monthly releases, critical updates as needed - **Changelog**: [CHANGELOG.md](./CHANGELOG.md) ### Contributing to Documentation We welcome contributions to improve our documentation: 1. **Fork** the documentation repository 2. **Create** a feature branch for your changes 3. **Submit** a pull request with clear description 4. **Review** process typically takes 2-3 business days ## Ready to Get Started? Choose your path: ### **I want to try it now** → [Quick Start Guide](./tutorials/quick-start.md) ### **I need complete documentation** → [API Reference](./api/README.md) ### **I'm evaluating for purchase** → [Main Overview](./README.md) ### **I need production deployment** → [Production Guide](./guides/production.md) --- **Welcome to GoCharting SDK!** We're excited to help you build amazing trading applications. If you have any questions, don't hesitate to reach out to our team. _Happy coding!_ File: docs/api/chart-widget.md ========================================= # Chart Widget API Imperative helpers on the object returned by [`createChart()`](./chart.md). Use these for quick symbol/interval/theme changes, multichart `*AtIndex` calls, broker book updates, and resubscription. For Phase 0–5 widget APIs (`activeChart()`, `setLayout()`, `subscribe()`, `applyOverrides()`, persistence, …), see the **[Chart API](./chart.md)** — that is the primary surface. > [!TIP] > > Prefer the **`createChart` return value** (`chart`). Avoid relying on `getChartInstance()` / the `onReady` ProfessionalChart ref for new code — those expose the same legacy helpers without the Phase 0–5 façade. ## Quick start ```javascript import { createChart } from "@gocharting/chart-sdk"; const chart = createChart("#chart-container", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", onReady: () => { // Chart is mounted — call methods on `chart` (the createChart return value) chart.setSymbol("BYBIT:FUTURE:ETHUSDT"); chart.setInterval("1h"); chart.setTheme("dark"); }, }); // Same helpers are available immediately after createChart returns // (they throw until the internal chart is ready) ``` ### Preferred vs legacy style ```javascript // Preferred (Phase 0–5) — per-pane API chart.activeChart().setSymbol("MSFT"); chart.chart(1).setInterval("15m"); chart.setLayout("1|1"); // Convenience on the widget (still supported) chart.setSymbol("MSFT"); chart.setIntervalAtIndex("15m", 1); ``` See [Chart API → Widget façade](./chart.md#widget-façade). --- ## Access patterns | Access | What you get | | ------ | ------------ | | `const chart = createChart(...)` | Full widget: Phase 0–5 façade **plus** the helpers below | | `chart.getChartInstance()` | Internal `ProfessionalChart` — same legacy helpers only | | `onReady` callback arg | Same internal instance as `getChartInstance()` | | `` | Limited React ref — see [GoCharting Component](./gocharting-component.md) | --- ## Basic control | Method | Description | | ------ | ----------- | | `destroy()` | Tear down the chart and subscriptions | | `isDestroyed()` | Whether `destroy()` already ran | | `setSymbol(symbol)` | Symbol on the **active** chart | | `setInterval(interval)` | Interval on the **active** chart | | `setTheme("light" \| "dark")` | Named theme (see [Themes](./themes.md); hex via `themeColor` / `setTheme` behavior there) | | `getAllCharts()` | `{ isMultichartingEnabled, charts }` snapshot | ```javascript chart.setSymbol("BYBIT:FUTURE:BTCUSDT"); chart.setInterval("5m"); chart.setTheme("dark"); ``` Resize the **container** (or rely on AutoFit). There is no supported `resize()` API. --- ## Multichart (`*AtIndex`) 0-based indexes. Prefer `chart.chart(index)` / `setActiveChart(index)` from the [Chart API](./chart.md) when you need the full `IChartApi`. | Method | Description | | ------ | ----------- | | `setChartSymbolAtIndex(symbol, idx)` | Symbol on chart `idx` | | `setIntervalAtIndex(interval, idx)` | Interval on chart `idx` | | `setChartTypeAtIndex(type, idx)` | Chart type on chart `idx` | | `addIndicatorAtIndex(indicator, idx)` | Add study on chart `idx` | | `addDrawingAtIndex(drawing, idx, chartId?)` | Add drawing on chart `idx` | | `deleteObjectAtIndex(objectId, idx, chartId?)` | Remove entity on chart `idx` | | `updateSettingsAtIndex(settings, idx)` | Merge settings on chart `idx` | | `getChartStateAtIndex(idx)` / `setChartStateAtIndex(state, idx)` | Pane state snapshot / restore | ```javascript chart.setLayout?.("2"); // or use layout chooser chart.setChartSymbolAtIndex("ETHUSDT", 0); chart.setChartSymbolAtIndex("BTCUSDT", 1); chart.setIntervalAtIndex("15m", 1); ``` --- ## Indicators & drawings | Method | Description | | ------ | ----------- | | `addIndicator({ type, id, … })` | Add study on the **active** chart (GC type ids, e.g. `"RSI"`, `"EMA"`) | | `addDrawing(drawing, chartId?)` | Add drawing on active chart (`chartId` default `"MAIN_CHART"`) | | `deleteObject(objectId, chartId?)` | Remove study or drawing | ```javascript chart.addIndicator({ type: "RSI", id: "rsi-1" }); chart.addDrawing({ type: "trendline", name: "Support", options: {}, appearance: { color: "#00FF00", lineWidth: 2 }, }); chart.deleteObject("rsi-1"); ``` For richer shape/study APIs (`createShape`, `createStudy`, templates, z-order), use [`chart.activeChart()`](./chart.md). --- ## Settings & state | Method | Description | | ------ | ----------- | | `updateSettings(partial)` | Merge settings on the active chart | | `getChartState()` / `setChartState(state)` | Snapshot / restore active chart state | ```javascript chart.updateSettings({ zone: "America/New_York" }); const state = chart.getChartState(); // …persist… chart.setChartState(state); ``` Named layout persistence / autosave adapters: [Configuration](./configuration.md) + [Events](./events.md) (`onAutoSaveNeeded`). --- ## Trading | Method | Description | | ------ | ----------- | | `setBrokerAccounts(data)` | Push accounts / orders / trades / positions into the chart | | `updatePositions(positions)` | Patch positions without a full book rebuild (when available) | ```javascript chart.setBrokerAccounts({ accountList: [{ id: "1", name: "Demo", balance: 10000, currency: "USD" }], orderBook: [], tradeBook: [], positions: [], }); ``` Requires `trading.enableTrading: true`. Handle UI actions via [`appCallback`](./events.md). Details: [Trading API](./trading.md). --- ## Resubscription ### `resubscribeAll(idToken?)` Rehydrate candles, ticks, compare symbols, and related feeds after a long tab hide, WebSocket reconnect, or auth refresh. ```javascript document.addEventListener("visibilitychange", () => { if (!document.hidden) { chart.resubscribeAll(); } }); // Optional new auth token chart.resubscribeAll(newIdToken); ``` --- ## Method summary | Category | Methods | | -------- | ------- | | Lifecycle | `destroy`, `isDestroyed`, `getChartInstance` | | Active chart | `setSymbol`, `setInterval`, `setTheme`, `getAllCharts` | | Multichart | `setChartSymbolAtIndex`, `setIntervalAtIndex`, `setChartTypeAtIndex`, `*AtIndex` variants | | Studies / drawings | `addIndicator`, `addDrawing`, `deleteObject` (+ `*AtIndex`) | | Settings / state | `updateSettings`, `getChartState`, `setChartState` (+ `*AtIndex`) | | Trading | `setBrokerAccounts`, `updatePositions` | | Data | `resubscribeAll` | Phase 0–5 façade (`activeChart`, `layout`, `subscribe`, overrides, features, save/load, …): **[Chart API](./chart.md)**. --- ## Related - **[Chart API](./chart.md)** — primary `createChart` façade (Phase 0–5) - **[Configuration](./configuration.md)** — `ChartConfig` / construct options - **[Events](./events.md)** — `appCallback`, widget `subscribe` - **[Trading](./trading.md)** — broker book + order events - **[Themes](./themes.md)** — `theme` / `themeColor` / `setTheme` - **[GoCharting Component](./gocharting-component.md)** — declarative React API - **[Types](./types.md)** — TypeScript definitions File: docs/api/chart.md ================================== # Chart Creation API The GoCharting SDK provides a simple `createChart()` function for creating professional trading charts with **built-in AutoFit** and real-time capabilities. ## Usage ### Simple Function Call (Recommended) ```javascript import { createChart } from "@gocharting/chart-sdk"; const chart = createChart("#chart-container", { symbol: "AAPL", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", }); ``` ### Declarative React Component ```jsx ``` See the **[GoCharting React Component](./gocharting-component.md)** documentation for the declarative API. ## API Reference ### `createChart(container, config)` Creates a new chart instance with the specified configuration. #### Syntax ```typescript function createChart( container: HTMLElement | string, config: ChartConfig ): ChartInstance; ``` #### Parameters | Parameter | Type | Required | Description | | ----------- | ----------------------- | -------- | ----------------------------------------------- | | `container` | `HTMLElement \| string` | | DOM element or CSS selector for chart container | | `config` | `ChartConfig` | | Chart configuration object | #### Configuration Object | Parameter | Type | Required | Description | | ------------- | ------------------ | -------- | ------------------------------------------------- | | `datafeed` | `object` | | Datafeed object with required methods | | `symbol` | `string` | | Initial symbol to display | | `interval` | `string` | | Initial time interval | | `licenseKey` | `string` | | Your SDK license key | | `theme` | `string` | | Chart theme ('light' or 'dark', default: 'light') | | `locale` | `string` | | Locale for translations (default: 'en-US') | | `autosize` | `boolean` | | Enable AutoFit (default: true) | | `width` | `number \| string` | | Chart width (default: '100%') | | `height` | `number \| string` | | Chart height (default: '100%') | | `trading` | `object` | | Trading configuration (`trading.enableTrading`, …) | | `appCallback` | `function` | | Callback for trading and app events | | `onReady` | `function` | | Callback when chart is ready | | `onError` | `function` | | Callback for errors | See the **[Configuration API](./configuration.md)** for the complete [`ChartConfig`](./types.md#chartconfig) (Phase 4 construct options, Phase 5 persistence, trading, mobile, …). #### Returns Returns a `ChartInstance` object. All methods are listed here and documented in detail below: | Category | Methods | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | Lifecycle | `destroy()`, `isDestroyed()`, `getChartInstance()` | | Widget façade | `activeChart()`, `chart(index)`, `chartsCount()`, `activeChartIndex()`, `setActiveChart(index)`, `layout()`, `setLayout(layout)`, `layoutName()`, `setLayoutSizes(sizes)`, `resetLayoutSizes()`, `unloadUnusedCharts()`, `save()` / `load()`, `getSavedCharts()`, `saveChartToServer()` / `loadChartFromServer()` / `removeChartFromServer()`, `showSaveAsChartDialog()` / `showLoadChartDialog()`, `applyOverrides()`, `applyStudiesOverrides()`, `applyTradingCustomization()`, `setCSSCustomProperty()`, `getCSSCustomPropertyValue()`, `addCustomCSSFile()`, `customThemes()`, `features()`, `setFeatureEnabled()`, `getFeatureEnabled()`, `getFeatures()`, `headerReady()`, `createButton()`, `createDropdown()`, `removeButton()`, `onShortcut()`, `onContextMenu()`, `closePopupsAndDialogs()`, `showConfirmDialog()`, `showNoticeDialog()`, `selectLineTool()`, `selectedLineTool()`, `hideAllDrawingTools()`, `lockAllDrawingTools()`, `magnetEnabled()`, `magnetMode()`, `takeClientScreenshot()`, `takeScreenshot()`, `clearUndoHistory()`, `undoRedoState()`, `resetCache()`, `getStudiesList()`, `getStudyInputs()`, `getStudyStyles()`, `supportedChartTypes()`, `getIntervals()`, `symbolInterval()`, `mainSeriesPriceFormatter()`, `currencyAndUnitVisibility()`, `customSymbolStatus()`, `navigationButtonsVisibility()`, `paneButtonsVisibility()`, `dateFormat()`, `timeHoursFormat()`, `symbolSync()`, `intervalSync()`, `drawOnAllChartsEnabled()`, `crosshairSync()`, `dateRangeSync()`, `timeSync()`, `subscribe()`, `unsubscribe()`, `getAllCharts()` | | Chart control | `setSymbol()`, `setInterval()`, `setTheme()`, `setChartType()`, `resize()`, `goToDate()`, `setTimezone()`, `getTimezonePresets()`, `getCurrentTimezone()` — also [Chart Widget](./chart-widget.md) | | Multichart (by index) | `setChartSymbolAtIndex()`, `setIntervalAtIndex()`, `setChartTypeAtIndex()`, `addIndicatorAtIndex()`, `addDrawingAtIndex()`, `deleteObjectAtIndex()`, `updateSettingsAtIndex()`, `getChartStateAtIndex()`, `setChartStateAtIndex()` — also [Chart Widget](./chart-widget.md) | | Objects & settings | `addIndicator()`, `addDrawing()`, `deleteObject()`, `updateSettings()`, `getChartState()`, `setChartState()` | | Templates | `saveTemplate()`, `applyTemplate()`, `getTemplates()`, `deleteTemplate()` | | Trading data | `setBrokerAccounts()`, `updatePositions()` | | Data / connection | `resubscribeAll()` | | OI Profile | `setOIProfile()`, `getOIProfileSettings()` | > [!NOTE] > > Prefer `activeChart()` / `chart(index)` for per-pane work. Convenience helpers (`setSymbol`, `*AtIndex`, …) are summarized on **[Chart Widget](./chart-widget.md)**. > Most methods throw if called after `destroy()` or before the chart is ready — wait for `onReady` or use the returned instance after mount. ### Built-in AutoFit Features - **Zero Configuration** - Works out of the box - **Fully Responsive** - Adapts to any container size - **Bulletproof Sizing** - Handles all edge cases - **Automatic CSS** - No external stylesheets needed - **Real-time Resizing** - ResizeObserver integration ### Example (Simplified API - Recommended) ```javascript import { createChart } from "@gocharting/chart-sdk"; // Create your datafeed object (no inheritance!) const myDatafeed = { async getBars(symbolInfo, resolution, periodParams) { // Fetch your market data return { bars: [...] }; }, resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "NASDAQ"; const segment = parts.length >= 3 ? parts[1] : "EQUITY"; const symbol = parts[parts.length - 1] || "AAPL"; const name = symbol; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name, asset_type: "EQUITY", source_id: symbol, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.01, display_tick_size: 0.01, volume_size_increment: 1, max_tick_precision: 2, max_volume_precision: 0, quote_currency: "USD", supports: { footprint: false }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "America/New_York", hours: [ { open: false }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: false }, ], valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: "stock", session: "0930-1600", timezone: "America/New_York", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); }, searchSymbols(userInput, exchange, symbolType, callback) { // Search for symbols callback(searchResults); } }; // Create chart with one simple call! const chart = createChart('#chart-container', { symbol: "NASDAQ:AAPL", interval: "1D", datafeed: myDatafeed, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", theme: "light", onReady: () => console.log("Chart ready!"), onError: (error) => console.error("Chart error:", error) }); // Always clean up when done chart.destroy(); ``` ### Example (With Trading Features) ```javascript // Create chart with trading enabled const tradingChart = createChart("#trading-chart", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: myDatafeed, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", theme: "dark", trading: { enableTrading: true, // Enable trading context menus }, onReady: () => console.log("Trading chart ready!"), onError: (error) => console.error("Chart error:", error), }); // Trading features now available: // - Right-click context menu shows Buy/Sell options // - CrossHair menu shows trading options // - Settings > Trading tab is visible ``` ### Example (Declarative React Component) ```jsx import { GoCharting } from "@gocharting/chart-sdk"; function App() { return ( ); } ``` See **[GoCharting React Component](./gocharting-component.md)** for the full props reference and ref-based imperative access. ## Methods ### Lifecycle #### `destroy()` Destroys the chart and cleans up all resources including active subscriptions. ```javascript chart.destroy(); ``` **What it cleans up:** - **Active tick subscriptions** - Unsubscribes from all WebSocket streams - **Event listeners** - Removes all DOM event listeners - **ResizeObserver** - Disconnects resize monitoring - **React tree** - Unmounts the chart component from the container - **Memory references** - Prevents memory leaks **Important Notes:** - Always call `destroy()` when removing the chart from the DOM - Calling `destroy()` on an already-destroyed chart logs a warning and returns (no error) - Required for proper cleanup when recreating charts - Should be called in React `componentWillUnmount` or `useEffect` cleanup **Example with React:** ```javascript useEffect(() => { const chart = createChart("#chart", config); return () => chart.destroy(); // Cleanup on unmount }, []); ``` #### `isDestroyed()` Returns `true` if the chart has been destroyed. ```javascript if (!chart.isDestroyed()) { chart.setSymbol("MSFT"); } ``` **Returns:** `boolean` #### `getChartInstance()` Returns the underlying chart component instance (advanced use only). ```javascript const instance = chart.getChartInstance(); ``` **Returns:** The internal `ProfessionalChart` ref, or `null` if not mounted. ### Widget façade Widget entry points on the `createChart` return value. > **Principles:** Payloads use **GoCharting field names**. Prefer `activeChart()` / `chart(index)` over Redux or `getChartInstance()`. **Shipped:** Phase 0–1E + Phase 2 + Phase 3 + Phase 4 construct options + Phase 5 persistence. **Convenience helpers** (`setSymbol`, `*AtIndex`, `setBrokerAccounts`, …): also listed on **[Chart Widget](./chart-widget.md)**. Interactive labs: [`phase0-1a-api-lab.html`](../../examples/phase0-1a-api-lab.html) ([CodePen](https://codepen.io/Admin-GoCharting/pen/xbgJXEv)), [`phase1b-api-lab.html`](../../examples/phase1b-api-lab.html) ([CodePen](https://codepen.io/Admin-GoCharting/pen/myRjBRO)), [`phase1c-api-lab.html`](../../examples/phase1c-api-lab.html) ([CodePen](https://codepen.io/Admin-GoCharting/pen/rajrGjR)), [`phase1d-api-lab.html`](../../examples/phase1d-api-lab.html) ([CodePen](https://codepen.io/Admin-GoCharting/pen/dPNjVvG)), [`phase1e-api-lab.html`](../../examples/phase1e-api-lab.html) ([CodePen](https://codepen.io/Admin-GoCharting/pen/zxNLEZQ)), [`phase2-layout-lab.html`](../../examples/phase2-layout-lab.html) ([CodePen](https://codepen.io/Admin-GoCharting/pen/zxNLEwQ)), [`phase3-overrides-lab.html`](../../examples/phase3-overrides-lab.html), [`phase4-constructor-lab.html`](../../examples/phase4-constructor-lab.html) ([CodePen](https://codepen.io/Admin-GoCharting/pen/RNKBLxp)), [`phase5-persistence-lab.html`](../../examples/phase5-persistence-lab.html) ([CodePen](https://codepen.io/Admin-GoCharting/pen/gbgjGvx)), [`phase6-subscribeBars-lab.html`](../../examples/phase6-subscribeBars-lab.html), [`phase6-searchSymbolsPaginated-lab.html`](../../examples/phase6-searchSymbolsPaginated-lab.html), [`phase6-onReady-udf-lab.html`](../../examples/phase6-onReady-udf-lab.html), [`tv-parity-construct-lab.html`](../../examples/tv-parity-construct-lab.html), [`tv-parity-adapters-lab.html`](../../examples/tv-parity-adapters-lab.html), [`leftovers-api-lab.html`](../../examples/leftovers-api-lab.html). See also **[Widget / IChartApi types](/api/types#widget--ichartapi-types-phase-0–1e)** for TypeScript definitions. #### `activeChart()` / `chart(index)` Returns an `IChartApi` bound to the active chart or a specific 0-based index. ```javascript const active = chart.activeChart(); console.log(active.symbol(), active.interval()); active.setSymbol("MSFT"); active.setInterval("5m"); // setResolution / resolution() remain as aliases of setInterval / interval chart.chart(1).setChartType("candle"); chart.chart(0).addIndicator({ type: "SMA", id: "sma-1" }); ``` **Returns:** `IChartApi` #### `chartsCount()` / `activeChartIndex()` / `setActiveChart(index)` ```javascript console.log(chart.chartsCount(), chart.activeChartIndex()); chart.setActiveChart(1); // emits activeChartChanged / CHART_SELECTED ``` | Method | Returns | Description | | ----------------------- | --------- | ------------------------------------------------ | | `chartsCount()` | `number` | Charts in the current layout | | `activeChartIndex()` | `number` | 0-based index of the selected chart | | `setActiveChart(index)` | `void` | Select chart; emits `activeChartChanged` | #### `layout()` / `setLayout(layout)` Multi-chart grid arrangement. GoCharting layout ids are canonical; common layout aliases are accepted by `setLayout`. ```javascript console.log(chart.layout()); // e.g. "1" chart.subscribe("layout", (info) => { console.log(info.layout, info.charts.length, info.isMultichartingEnabled); }); chart.setLayout("1|1"); // two stacked charts chart.setLayout("2h"); // TV alias → GoCharting "2" console.log(chart.chartsCount()); // 2 ``` | Method | Returns | Description | | ------ | ------- | ----------- | | `layout()` | `string` | Current GoCharting layout id | | `setLayout(layout)` | `void` | Apply layout; emits `layout` / `CHART_MODE_CHANGED` | | `layoutName()` | `string \| null` | Current named layout title (Phase 5), or `null` | **GoCharting ids:** `1`, `1|1`, `2`, `1|1|1`, `3`, `1-2`, `2|1`, `1|2`, `2-1`, `2|2`, `4`, `1|1|1|1`, `1-3`, `3-1`, `1|3`, `3|1`. **TV aliases (mapped):** `s`→`1`, `2h`→`2`, `2v`→`1|1`, `3h`→`3`, `3v`→`1|1|1`, `4h`→`4`, `4v`→`1|1|1|1`, `2-2`→`2|2`. Ids that already match GoCharting (`2-1`, `1-2`, `1-3`, `3-1`, `4`) work as-is. #### `setLayoutSizes(sizes)` / `resetLayoutSizes()` Programmatic multi-chart pane sizes (P2-2). Same persistence as drag-resize (`sessionStorage` key `chartLayoutState`). Percentages should match the current layout’s split counts (e.g. `"1|1"` → `rows: [40, 60]`; `"2"` → `columns: [30, 70]`). ```javascript chart.setLayout("1|1"); chart.setLayoutSizes({ rows: [35, 65] }); chart.resetLayoutSizes(); // equal splits again ``` | Method | Notes | | ------ | ----- | | `setLayoutSizes({ rows?, columns? }, disableUndo?)` | At least one of `rows` / `columns`; values must be finite `> 0`. `disableUndo` ignored (TV parity). | | `resetLayoutSizes(disableUndo?)` | Clears persisted sizes; panes return to equal splits | #### `unloadUnusedCharts()` Dispose non-visible charts soft-stashed after a layout shrink (P2-6 / TV `unloadUnusedCharts`). GoCharting already drops panes from the live `chartComponentList` on shrink; orphans are held until this call so hosts can free stores/observers. Also clears chart event observers beyond `chartsCount()` and flushes `sessionStorage` `GoChartingSDK.CompleteChartLayout` to the visible layout. Expanding a layout still clones the last visible chart (no TV soft-restore of prior panes). ```javascript chart.subscribe("layout", () => { chart.unloadUnusedCharts(); }); chart.setLayout("1"); // after "2|2" ``` | Method | Notes | | ------ | ----- | | `unloadUnusedCharts()` | Idempotent; safe when there are no stashed orphans | #### Persistence (Phase 5) Serialize / restore layouts and named charts. Construct options (`save_load_adapter`, `auto_save_delay`, `load_last_chart`, `saved_data`, `snapshot_url`) are documented under [Configuration → Persistence](./configuration.md#persistence). Lab: [`phase5-persistence-lab.html`](../../examples/phase5-persistence-lab.html). ```javascript const blob = chart.save(); await chart.load(blob); chart.subscribe("onAutoSaveNeeded", () => { chart.saveChartToServer({ name: chart.layoutName() || "Autosave" }); }); const id = await chart.saveChartToServer({ name: "My layout" }); await chart.loadChartFromServer(id); console.log(chart.layoutName()); ``` | Method | Returns | Notes | | ------ | ------- | ----- | | `save(callback?)` | layout blob | Full multi-chart snapshot; optional callback | | `load(state)` | `Promise` | Restore GC layout blob (or JSON string) | | `getSavedCharts()` | `Promise` | Named charts via `save_load_adapter` | | `saveChartToServer({ name?, id? })` | `Promise` | Persist named chart; returns id | | `loadChartFromServer(id)` | `Promise` | Load named chart | | `removeChartFromServer(id)` | `Promise` | Delete named chart | | `layoutName()` | `string \| null` | Current named layout title | | `showSaveAsChartDialog()` / `showLoadChartDialog()` | `void` | Host prompts for save/load (adapter-backed) | Also subscribe to `onAutoSaveNeeded`, `onChartLoaded`, `chart_load_requested` — see [Events](./events.md). #### `applyOverrides()` / `applyStudiesOverrides()` / `applyTradingCustomization()` Runtime theming / study / trading merges (P3-1). TV-like method names; payloads use **GoCharting nested objects** (not TV dotted paths like `paneProperties.background`). ```javascript // Chart appearance — all live charts chart.applyOverrides({ background: { backgroundColor: "#1a1a2e" }, }); // or wrapped: chart.applyOverrides({ appearance: { background: { backgroundColor: "#1a1a2e" } }, }); // Studies by type — live SERIES + objectTemplates for future adds. // Flat `{ strokeStyle }` is nested under appearance.EMA / appearance.RSI automatically. chart.activeChart().addIndicator({ type: "EMA", name: "EMA" }); chart.applyStudiesOverrides({ EMA: { strokeStyle: "rgba(255, 159, 67, 1)" }, RSI: { appearance: { strokeStyle: "rgba(29, 209, 161, 1)" } }, }); // Root trading prefs (same shape as construct-time `trading`) chart.applyTradingCustomization({ enableTrading: true, boxAlignment: "left" }); ``` | Method | Notes | | ------ | ----- | | `applyOverrides(partial)` | Deep-merges into every chart’s `config.appearance` | | `applyStudiesOverrides(map)` | Keys = indicator type (`"EMA"`, …); flat plot styles nest under `appearance[TYPE]`; updates live SERIES + `objectTemplates` | | `applyTradingCustomization(partial)` | Deep-merges root `trading` via SDK config overrides | Lab: [`phase3-overrides-lab.html`](../../examples/phase3-overrides-lab.html). #### `setCSSCustomProperty` / `getCSSCustomPropertyValue` / `addCustomCSSFile` / `customThemes()` Runtime CSS theming for the **chart container** (P3-2). Names must start with `--`. These APIs do **not** retheme the canvas series — use `setTheme` / `applyOverrides` for that. ```javascript chart.setCSSCustomProperty("--gc-accent", "#3d8bfd"); console.log(chart.getCSSCustomPropertyValue("--gc-accent")); // "#3d8bfd" chart.addCustomCSSFile("./phase3-custom-theme.css"); // removed on destroy const themes = await chart.customThemes(); themes.applyCustomThemes({ "--gc-panel-bg": "#1a2332", "--gc-accent": "#ff9f43", }); themes.resetCustomThemes(); ``` | Method | Notes | | ------ | ----- | | `setCSSCustomProperty(name, value)` | Sets on chart container `style` | | `getCSSCustomPropertyValue(name)` | Inline or computed value (`""` if unset) | | `addCustomCSSFile(url)` | Idempotent `` inject; cleaned up on `destroy()` | | `customThemes()` | `Promise` → `{ applyCustomThemes, resetCustomThemes, getCustomThemes }` | #### `features()` / `setFeatureEnabled` / `getFeatureEnabled` / `getFeatures()` Runtime chrome toggles (P3-3) beyond construct-time `exclude` / `disableSearch` / `disableCompare` / `hideDrawingToolBar`. Construct-time TV featuresets (`disabled_features` / `enabled_features`) are wired in P4-1 — see Configuration. ```javascript chart.setFeatureEnabled("topBar", false); chart.setFeatureEnabled("header_symbol_search", false); // TV alias chart.features().setValue({ drawingToolbar: false, compare: false }); console.log(chart.getFeatures()); ``` | Feature id | Effect | | ---------- | ------ | | `topBar` | Desktop top bar | | `drawingToolbar` | Left drawings menu | | `symbolSearch` | Top-bar search | | `compare` | Compare button | | `bottomBar` / `leftPanel` / `rightPanel` | Feature map / `exclude` (not all shells render every panel) | TV aliases: `header_widget`, `left_toolbar`, `drawing_toolbar`, `header_symbol_search`, `header_compare`, `bottom_toolbar`. #### `headerReady()` / `createButton` / `createDropdown` / `removeButton` Custom top-toolbar controls (P3-4). Prefer `headerReady()` before `createButton`. ```javascript await chart.headerReady(); const btn = chart.createButton({ align: "left" }); btn.textContent = "Ping"; btn.addEventListener("click", () => console.log("ping")); const dd = await chart.createDropdown({ title: "TF", align: "right", items: [ { title: "5m", onSelect: () => chart.setInterval("5m") }, { title: "1h", onSelect: () => chart.setInterval("1h") }, ], }); chart.removeButton(btn); ``` #### `onShortcut` / `onContextMenu` Keyboard shortcuts and chart context-menu injection (P3-5). Shortcuts are ignored while focus is in an input/textarea. ```javascript const unsub = chart.onShortcut("alt+q", () => console.log("alt+q")); // chart.onShortcut(["alt", 81], () => {}); chart.onContextMenu((unixtime, price) => [ { position: "top", text: "Alert at price", click: () => console.log(unixtime, price), }, { text: "-" }, { position: "bottom", text: "Host action", click: () => {} }, { text: "-Reset chart" }, // hide built-in (best-effort) ]); ``` #### `closePopupsAndDialogs` / `showConfirmDialog` / `showNoticeDialog` Dismiss open UI and show host dialogs (P3-6). ```javascript chart.closePopupsAndDialogs(); const ok = await chart.showConfirmDialog({ title: "Accept terms?", body: "Continue only if you agree.", }); await chart.showNoticeDialog({ title: "Saved", body: "Layout stored locally.", }); ``` #### `selectLineTool` / hide / lock / magnet Drawing toolbar state (P3-7). ```javascript chart.selectLineTool("trend_line"); console.log(chart.selectedLineTool()); // "trend_line" chart.hideAllDrawingTools().setValue(true); chart.magnetEnabled().setValue(true); // magnetMode() is a 0/1 view over the same boolean — GC only supports // magnet on/off (no TV weak(0)/strong(1) engine). Prefer magnetEnabled(). ``` #### `takeClientScreenshot` / `takeScreenshot` Local capture and optional upload (P3-8). ```javascript const canvas = await chart.takeClientScreenshot(); const dataUrl = canvas.toDataURL("image/png"); // Requires construct-time snapshot_url const url = await chart.takeScreenshot(); chart.subscribe("onScreenshotReady", (imageUrl) => console.log(imageUrl)); ``` | Method | Notes | | ------ | ----- | | `takeClientScreenshot(options?)` | `Promise` — native canvases + ChartBrandLogo, then html2canvas | | `takeScreenshot()` | `Promise` — POST to `config.snapshot_url`; emits `onScreenshotReady` | | Top-bar camera | Built-in button (hide with `exclude.screenshot`); Download PNG / Copy to clipboard | #### `clearUndoHistory` / `undoRedoState` / `resetCache` ```javascript console.log(chart.undoRedoState()); chart.clearUndoHistory(); chart.resetCache(); // invalidate cached bars; does not refetch alone ``` #### `getStudiesList` / `getStudyInputs` / `getStudyStyles` Catalog metadata (not live instances — use `getAllStudies` for those). ```javascript const types = chart.getStudiesList(); // GC type ids, e.g. "RSI" console.log(chart.getStudyInputs("RSI")); console.log(chart.getStudyStyles("RSI")); chart.activeChart().addIndicator({ type: "RSI" }); ``` #### `supportedChartTypes` / `getIntervals` / `symbolInterval` / `getDatafeedConfiguration` Chart style ids are **strings** (not a numeric enum). See **[Enums — chart type strings](/api/enums#chart-type-strings)**. `getIntervals()` prefers the active symbol’s `valid_intervals`, then datafeed `onReady` → `supported_resolutions`, then the built-in catalog. ```javascript console.log(chart.supportedChartTypes()); // ["CANDLESTICK", "LINE", …] console.log(chart.getIntervals()); // ["1m", "5m", "1D", …] console.log(chart.symbolInterval()); // { symbol, interval } console.log(chart.getDatafeedConfiguration()); // from datafeed onReady (P6-1) ``` #### `mainSeriesPriceFormatter` / `currencyAndUnitVisibility` / `customSymbolStatus` `currencyAndUnitVisibility` and `customSymbolStatus` are painted as small chips next to the symbol search box (not just an in-memory API) — set a value and the chip appears/updates automatically. ```javascript console.log(chart.mainSeriesPriceFormatter().format(1234.5678)); chart.currencyAndUnitVisibility().setValue("alwaysOn"); // paints a "CCY" chip chart.customSymbolStatus().symbol("BTCUSDT").setVisible(true).setTooltip("Note"); ``` #### `navigationButtonsVisibility` / `paneButtonsVisibility` / `dateFormat` / `timeHoursFormat` ```javascript chart.navigationButtonsVisibility().setValue("alwaysOn"); chart.paneButtonsVisibility().setValue("alwaysOff"); chart.dateFormat().setValue("yyyy_mm_dd"); chart.timeHoursFormat().setValue("12"); ``` #### `symbolSync()` / `intervalSync()` Cross-chart sync controllers over `layoutStore` (`syncSymbols` / `syncIntervals`). Same behavior as the layout chooser toggles: turning **on** fans out the active chart’s symbol or interval. ```javascript chart.setLayout("1|1"); const sym = chart.symbolSync(); console.log(sym.value()); // false sym.setValue(true); // all charts take the active chart symbol sym.subscribe((on) => console.log("symbol sync", on)); chart.intervalSync().setValue(true); chart.setInterval("1h"); // fans out while sync is on ``` | Method | Returns | Notes | | ------ | ------- | ----- | | `symbolSync()` | `ISyncApi` | `value()` / `setValue(boolean)` / `subscribe(cb) → unregister` | | `intervalSync()` | `ISyncApi` | Same shape; maps to `syncIntervals` | #### `drawOnAllChartsEnabled()` / `crosshairSync()` / `dateRangeSync()` / `timeSync()` Additional sync controllers (P2-5 / P2-4). Same `ISyncApi` shape; turning **on** only flips the flag — ChartWidget / drawing code already honor them live (no fan-out). | Method | `layoutStore` key | Notes | | ------ | ----------------- | ----- | | `drawOnAllChartsEnabled()` | `syncDrawings` | TV alias for draw-on-all | | `crosshairSync()` | `syncCursors` | Crosshair / cursor sync | | `dateRangeSync()` | `syncDateRange` | Visible range sync | | `timeSync()` | `syncTime` | Time / scroll sync | ```javascript chart.drawOnAllChartsEnabled().setValue(true); chart.crosshairSync().setValue(true); chart.dateRangeSync().setValue(true); chart.timeSync().setValue(true); ``` #### `subscribe(event, callback)` / `unsubscribe(event, callback)` ```javascript const onActive = (msg) => console.log("active chart", msg.idx, msg.symbol); chart.subscribe("activeChartChanged", onActive); // also: chart.subscribe("layout", …) // from CHART_MODE_CHANGED // chart.subscribe("onAutoSaveNeeded", …) // Phase 5 // or raw: chart.subscribe("CHART_SELECTED", …) chart.unsubscribe("activeChartChanged", onActive); ``` | Event | Source | Notes | | --------------------- | ------------------------------ | -------------------------------------------------- | | `activeChartChanged` | `CHART_SELECTED` | Active pane changed | | `layout` | `CHART_MODE_CHANGED` | Multichart layout changed | | `onAutoSaveNeeded` | persistence | Debounced dirty — call `saveChartToServer` | | `onChartLoaded` | persistence | After `load` / restore | | `onGrayedObjectClicked` | `GRAYED_OBJECT_CLICKED` | A locked/grayed drawing was clicked — payload `{ objectId, chartId }` | | *(raw string)* | any `appCallback` `eventType` | e.g. `"CHART_SELECTED"` | Full event catalog: **[Events API](./events.md)**. Works alongside `appCallback`. #### `getAllCharts()` ```javascript const { isMultichartingEnabled, charts } = chart.getAllCharts(); ``` **Returns:** `{ isMultichartingEnabled: boolean, charts: ChartSelectedMessage[] }` --- #### IChartApi — Phase 0 (symbol / objects) Per-chart API from `activeChart()` / `chart(index)`. | Method | Description | | ------ | ----------- | | `chartIndex()` | 0-based index of this chart | | `symbol()` | Current symbol string, or `null` | | `interval()` / `resolution()` | Current interval (e.g. `"1m"`, `"1D"`); `resolution` is an alias | | `chartType()` | Current chart type string, or `null` | | `symbolInterval()` | `{ symbol, interval }` or `null` | | `setSymbol(symbol)` | Change symbol on this chart | | `setInterval(interval)` / `setResolution(interval)` | Change interval; `setResolution` is an alias | | `setChartType(chartType)` | Change chart type | | `addIndicator(indicator)` | Add an indicator (`{ type, id, … }`) | | `addDrawing(drawing, chartId?)` | Add a drawing (prefer Phase 1B `createShape` for new code) | | `deleteObject(objectId, chartId?)` | Delete drawing or indicator (prefer `removeEntity` for drawings) | | `updateSettings(settings)` | Patch chart settings | | `getChartState()` / `setChartState(state)` | Persist / restore full chart state | ```javascript const api = chart.activeChart(); console.log(api.chartIndex(), api.symbol(), api.interval(), api.chartType()); api.setSymbol("NASDAQ:MSFT"); api.setInterval("5m"); api.addIndicator({ type: "SMA", id: "sma-1" }); const state = api.getChartState(); ``` --- #### IChartApi — Phase 1A (viewport / panes / export) | Method | Semantics | | ------ | --------- | | `getVisibleRange()` / `setVisibleRange({ from, to })` | Visible time window in Unix **seconds** | | `getVisibleBarsRange()` | Visible bar indices `{ from, to }` in loaded data | | `goToDate(date)` | Jump to a date (`string` \| `Date` \| `{ date1, date2 }`) | | `setTimeFrame(timeframe)` | String presets like `"1D"`, or a `TimeFrameInput` object | | `zoomOut()` / `canZoomOut()` | Zoom out one step; `canZoomOut()` reports whether zoom-out is available | | `resetData()` | Resets viewport/scales; also triggers a real data refetch (cache invalidation + resubscribe) when the host wires it, in addition to the widget's own `resetChart()` | | `exportData({ includeVolume?, useFullData? })` | Returns `{ schema, data }` or `null` | | `getPanes()` | `PaneApi[]` — pane info plus `getHeight` / `setHeight`, `getMainSeries()`, `getSeries()` (`SeriesPaneApi`), `getStudies()`, `getOverlays()`, `moveTo` (GC ids, not TV `paneIndex`) | | `getAllPanesHeight()` / `setAllPanesHeight(heights)` | Get/set pane height fractions (`chartOrder` order). Pass either **independent panes only** or the full `chartOrder`. Independent (stacked) panes are normalized to sum to 1; dependent overlays (e.g. `VOLUME_CHART` on `MAIN_CHART`) keep a separate container fraction so the stack does not leave a bottom gap. Accepts fractions or relative weights. | | `getPriceToBarRatio()` / `setPriceToBarRatio(ratio)` | Get/lock the price-to-bar aspect ratio (`setPriceToBarRatio` sets `preserveAspectRatio = true`) | | `getTimeScale()` | `{ getVisibleRange(), barSpacing? }` | | `getSeries()` | Primary series as `ISeriesApi` (`id` / `type` / `title` / `symbol` / `interval` / chart style / visibility / `priceScale()`) or `null` | | `maximizeChart()` / `isMaximized()` | Maximize / query maximized pane state | | `symbolExt()` | Extended symbol info — includes `max_tick_precision`, `tick_size` (not TV `pricescale` / `minmov`). **Axis tick labels follow this symbol metadata** — GoCharting does not expose a TV-style host-controlled `pricescale`. | | `marketStatus()` | `{ isOpen, statusText, dataStatus, delaySeconds }` or `null` | | `inactivityGaps()` | Whether inactivity gaps are shown | | `setScrollEnabled(enabled)` / `setZoomEnabled(enabled)` | Enable/disable drag panning and mouse-wheel/pinch/toolbar zoom. Default `true` | | `clearMarks()` / `refreshMarks()` | Clear the current symbol's marks/events, or re-fetch them via `datafeed.getMarks` | ##### Series / pane / price scale (adapters) `PriceScaleApi` (via `series.priceScale()` / `getPriceScale()`) supports **y-scale mode**, **axis visibility**, and **price-to-bar ratio**. It does **not** let the host redefine axis tick formatting — that comes from symbol `tick_size` / `max_tick_precision`. ```javascript const api = chart.activeChart(); const series = api.getSeries(); series?.setChartStyle("candles"); series?.priceScale().setMode("log"); series?.priceScale().setVisible(true); const panes = api.getPanes(); const main = panes?.[0]; console.log(main?.id, main?.getMainSeries()?.symbol()); main?.getSeries()?.getPriceScale()?.setPriceToBarRatio(2); ``` Interactive lab: [`examples/tv-parity-adapters-lab.html`](../../examples/tv-parity-adapters-lab.html) (series / panes / studies / selection / groups / drawing events). Also [`examples/leftovers-api-lab.html`](../../examples/leftovers-api-lab.html) (viewport, drawings reload, marks, trading `updatePositions`, access gates, UI chips, resize/magnet/search delay). ```javascript const api = chart.activeChart(); const range = api.getVisibleRange(); // { from, to } Unix seconds if (range) { api.setVisibleRange({ from: range.from - 86400, to: range.to }); } api.setTimeFrame("1D"); api.zoomOut(); console.log(api.getVisibleBarsRange()); console.log(api.symbolExt()?.max_tick_precision, api.symbolExt()?.tick_size); console.log(api.getPanes()?.[0]?.id); // e.g. MAIN_CHART console.log(api.getAllPanesHeight()); api.setAllPanesHeight([0.7, 0.3]); // independent panes only, or full chartOrder // e.g. [0.7, 0.15, 0.15] with VOLUME dependent → main+study fill 100%; volume overlays console.log(api.getPriceToBarRatio()); api.setPriceToBarRatio(2); // locks 1 price unit == 2 bars api.setScrollEnabled(false); // disable panning api.setZoomEnabled(false); // disable wheel/pinch/toolbar zoom console.log(api.exportData({ includeVolume: true })?.data?.length); ``` --- #### IChartApi — Phase 1B (shapes / z-order / groups) ##### Creating shapes `createShape` / `createMultipointShape` / `createAnchoredShape` all accept `CreateShapeInput` and return an `objectId` string. **`CreateShapeInput` fields:** `type`, `name`, `shape`, `options`, `appearance`, `chartId`, `visible` - Aliases fill empty `options` / `appearance` from the drawing catalog. - `visible` defaults to `true` (required for the drawing to mount). - Time fields on `shape` use epoch **ms**; values in the seconds era are auto-normalized to ms. - If construct-time `drawings_access` (whitelist/blacklist by drawing type or name) denies the resolved type, `createShape` / `createMultipointShape` / `createAnchoredShape` throw. | Shape | `type` / `name` | Key `shape` fields | | ----- | --------------- | ------------------ | | Horizontal line | `HORIZONTAL_LINE` or `STRAIGHT_LINE` + name `"Horizontal Line"` | `shape.y` = **price** | | Trend line | `TRENDLINE` + name `"Line"` | `p1` / `p2`: `.x` = epoch **ms**, `.y` = price | | Text | `TEXT` + name `"Text"` | `shape: { x: ms, y: price }`; label via `appearance.text` | ```javascript const api = chart.activeChart(); const hlineId = api.createShape({ type: "HORIZONTAL_LINE", name: "Horizontal Line", shape: { y: 100 }, chartId: "MAIN_CHART", }); const lineId = api.createShape({ type: "TRENDLINE", name: "Line", shape: { p1: { x: Date.now() - 86400000, y: 98 }, p2: { x: Date.now(), y: 102 }, }, }); const textId = api.createShape({ type: "TEXT", name: "Text", shape: { x: Date.now(), y: 101 }, appearance: { text: "Support" }, }); ``` ##### Query / remove / persist | Method | Description | | ------ | ----------- | | `getAllShapes(chartId?)` | List drawings as `ShapeInfo[]` | | `getShapeById(objectId, chartId?)` | Single `ShapeInfo` or `null` | | `removeEntity(objectId, chartId?)` | Remove one drawing — prefer over `deleteObject` for drawings | | `removeAllShapes(chartId?)` | Remove all drawings on a pane (or all panes) | | `getLineToolsState()` | Snapshot `{ charts: { [chartId]: { objects, objectGroups } } }` | | `applyLineToolsState(state)` | Restore a line-tools snapshot | | `reloadLineToolsFromServer()` | Reloads the current named layout (drawings included) from `save_load_adapter` when one has been saved via `saveChartToServer()`; otherwise warns and leaves drawings unchanged (does not throw) | To restore drawings with a full layout, use `load()` / `loadChartFromServer(id)`, or `reloadLineToolsFromServer()` to reload the last-saved named layout in one call (Phase 5). ##### Z-order, selection, groups | Method | Description | | ------ | ----------- | | `bringToFront` / `bringForward` / `sendToBack` / `sendBackward` | Reorder a drawing by `objectId` | | `availableZOrderOperations(objectId, chartId?)` | `{ bringToFront, bringForward, sendToBack, sendBackward }` booleans | | `selection()` | `IGraphicSelectedApi` — `get()` / `set()` / `clear()` / `isEmpty()` / `showPropertiesDialog()` / `bringToFront()` / `sendToBack()` | | `showPropertiesDialog(objectId?, chartId?)` | Open the properties UI for a drawing | | `shapesGroupController()` | `{ createGroup, ungroup, getGroups, getGidDescription, getGroupVisibility, setGroupVisibility, getGroupsVisibility }` | | `onDrawingEvent()` | `ISubscription` — `create` / `remove` (also `widget.subscribe("drawing_event", …)`) | ```javascript const sel = api.selection(); sel.set({ chartId: "MAIN_CHART", objectId: hlineId }); console.log(sel.get()); const groups = api.shapesGroupController(); const gid = groups.createGroup([hlineId, lineId]); groups.setGroupVisibility(gid, false); console.log(groups.getGidDescription(gid), groups.getGroupsVisibility()); api.onDrawingEvent().subscribe(({ type, objectId }) => { console.log("drawing", type, objectId); }); chart.subscribe("drawing_event", ({ type, objectId, chartId }) => { console.log("widget drawing_event", type, objectId, chartId); }); ``` ##### End-to-end example (viewport + shapes + z-order) ```javascript const api = chart.activeChart(); // Viewport const range = api.getVisibleRange(); if (range) { api.setVisibleRange({ from: range.from - 86400, to: range.to }); } api.setTimeFrame("1D"); // Shapes const hlineId = api.createShape({ type: "HORIZONTAL_LINE", name: "Horizontal Line", shape: { y: 100 }, }); const lineId = api.createShape({ type: "TRENDLINE", name: "Line", shape: { p1: { x: Date.now() - 3_600_000, y: 98 }, p2: { x: Date.now(), y: 102 }, }, }); const textId = api.createShape({ type: "TEXT", name: "Text", shape: { x: Date.now(), y: 101 }, appearance: { text: "Note" }, }); api.bringToFront(hlineId); console.log(api.availableZOrderOperations(hlineId)); console.log(api.getAllShapes().length, api.selection().get()); const groupId = api.shapesGroupController().createGroup([hlineId, lineId]); console.log(api.shapesGroupController().getGroups()); console.log(api.shapesGroupController().getGidDescription(groupId)); ``` --- #### IChartApi — Phase 1C (studies / templates) Studies are `kind: "SERIES"` objects excluding `MAIN_SERIES` / `VOLUME_SERIES`. Create studies with existing `addIndicator({ type, name?, options?, appearance? })`. | Method | Description | | ------ | ----------- | | `getAllStudies(chartId?)` | List studies as `StudyInfo[]` (includes `chartId` pane id) | | `getStudyById(objectId, chartId?)` | `IStudyApi` or `null` — inputs/styles, visibility, `remove()` (searches all panes if `chartId` omitted) | | `removeAllStudies()` | Remove every study on this chart panel | | `createStudyTemplate(nameOrOptions?)` | Snapshot `{ id, name, indicators, createdAt }` and **persist** by default to `favourite.TEMPLATES` (Templates menu). Pass `{ name, save: false }` for a snapshot only (no menu save). | | `applyStudyTemplate(template)` | `removeAllStudies` then `addIndicator` each item (study ids change — re-resolve with `getStudyById` / `getAllStudies`) | | `loadChartTemplate(input)` | String → favourite `TEMPLATES` by name/id; `{ indicators }` → apply study template; ChartState blob (`getChartState()`) → `setChartState` | **Notes:** - Remove a single study with `deleteObject(id, chartId)` using `StudyInfo.chartId` from `getAllStudies` / `getStudyById`. Prefer `removeEntity` for drawings. - `deleteObject` / `removeEntity` search all panes when `chartId` is omitted, and **throw** if the id is not found. - After `applyStudyTemplate` / `loadChartTemplate`, prefer `getStudyById` (or re-list with `getAllStudies`) — applied studies get new ids. ```javascript const api = chart.activeChart(); api.addIndicator({ type: "SMA", name: "SMA" }); api.addIndicator({ type: "RSI", name: "RSI" }); const studies = api.getAllStudies(); console.log(studies.map((s) => ({ id: s.id, type: s.type, chartId: s.chartId }))); // Single-study remove (pane-aware) const rsi = studies.find((s) => s.type === "RSI"); if (rsi) api.deleteObject(rsi.id, rsi.chartId); const tpl = api.createStudyTemplate("My Mix"); // persists to Templates menu api.createStudyTemplate({ name: "temp", save: false }); // snapshot only api.removeAllStudies(); api.applyStudyTemplate(tpl); // ids changed — re-resolve const afterApply = api.getAllStudies(); const sma = afterApply.find((s) => s.type === "SMA"); if (sma) console.log(api.getStudyById(sma.id, sma.chartId)); const state = api.getChartState(); api.loadChartTemplate(state); // full chart restore ``` Interactive lab: [`examples/phase1c-api-lab.html`](../../examples/phase1c-api-lab.html). --- #### IChartApi — Phase 1D (trading lines, library-native) Chart overlay lines **independent of Broker / `setBrokerAccounts`**. Fields use GoCharting shapes (`size`, `side`, `shape.y`). Library order/position lines are tagged `options.source: "library"` / `libraryNative: true` so broker book refresh and `DELETE_ALL_ORDERLINES` do **not** wipe them. Setters (`setPrice`, `setText`, …) preserve `forTicker` and related drawing fields so the line stays mounted after the first update (drawings only render when `forTicker` matches the chart symbol). | Method | Returns | Notes | | ------ | ------- | ----- | | `createOrderLine()` | `IOrderLineApi` | Fluent: `setPrice` / `setText` / `setQuantity` / `setTooltip`, `onModify` / `onMove` / `onCancel`, `remove()` | | `createPositionLine()` | `IPositionLineApi` | Same setters + `onClose` / `onReverse` / `onModify`, `remove()` | | `createExecutionShape()` | `IExecutionShapeApi` | `setPrice` / `setTime` (unix sec or ms) / `setDirection("buy"\|"sell")` / `setText`, `remove()` | ```javascript const api = chart.activeChart(); const order = api .createOrderLine() .setPrice(65000) .setText("Limit buy") .setQuantity(1) .onMove(() => console.log("moved", order.getPrice())) .onCancel(() => console.log("cancel clicked")); const pos = api .createPositionLine() .setPrice(64000) .setQuantity(2) .setText("Long") .onClose(() => console.log("close")); const fill = api .createExecutionShape() .setPrice(64500) .setTime(Math.floor(Date.now() / 1000)) .setDirection("buy"); // order.remove(); pos.remove(); fill.remove(); ``` Interactive lab: [`examples/phase1d-api-lab.html`](../../examples/phase1d-api-lab.html). --- #### IChartApi — Phase 1E (events + actions) TV-like subscription objects: `subscribe(cb)` returns an unregister function; `unsubscribe(cb?)` removes one or all listeners. Event observers are cleaned up on `chart.destroy()`. | Method | Returns / args | Notes | | ------ | -------------- | ----- | | `onSymbolChanged()` | `ISubscription` | Fires when chart security symbol changes | | `onIntervalChanged()` | `ISubscription` | Fires when interval changes | | `onChartTypeChanged()` | `ISubscription` | Fires when chart type changes | | `onDataLoaded()` | `ISubscription` | Fires on `loading` true → false | | `dataReady(cb)` | `void` | One-shot when data is ready (not loading + bars present) | | `onVisibleRangeChanged()` | `ISubscription<{ from, to }>` | Unix seconds; polled ~200ms while subscribed | | `crossHairMoved()` | `ISubscription<{ time, price }>` | Polled ~50ms while subscribed; fields may be `null` | | `onHoveredSourceChanged()` | `ISubscription` | `{ entityId, chartId?, type? }` or `null` | | `executeActionById(id)` | `void` | Allowlist only (throws otherwise) — see below | | `requestSelectBar()` | `Promise<{ index, time }>` | Resolves on next chart click; move crosshair over a bar first | | `cancelSelectBar()` | `void` | Rejects the pending select-bar promise | **`executeActionById` allowlist:** `chartReset`, `zoomIn`, `zoomOut`, `undo`, `redo`, `invertScale`, `logScale`, `magnet` (`ChartActionId`). See **[Enums & literal unions](/api/enums#chartactionid)**. ```javascript const api = chart.activeChart(); const unsub = api.onSymbolChanged().subscribe(() => { console.log("symbol →", api.symbol()); }); api.dataReady(() => console.log("bars ready")); api.onVisibleRangeChanged().subscribe((r) => { console.log("range", r.from, r.to); }); api.crossHairMoved().subscribe(({ time, price }) => { // throttle in your UI if needed }); api.executeActionById("zoomIn"); api.executeActionById("magnet"); api.requestSelectBar() .then(({ index, time }) => console.log("bar", index, time)) .catch((e) => console.warn(e.message)); // api.cancelSelectBar(); // unsub(); ``` Interactive lab: [`examples/phase1e-api-lab.html`](../../examples/phase1e-api-lab.html). ### Chart Control Convenience helpers on the `createChart` return value (active chart). Prefer `activeChart()` for per-pane APIs. Full convenience catalog: **[Chart Widget](./chart-widget.md)**. #### `setSymbol(newSymbol)` Changes the displayed symbol on the currently selected chart. ```javascript chart.setSymbol("NASDAQ:MSFT"); chart.setSymbol("BYBIT:FUTURE:BTCUSDT"); ``` **Parameters:** - `newSymbol` (string): Symbol name (typically `EXCHANGE:SEGMENT:SYMBOL`) #### `setInterval(newInterval)` Changes the chart time interval. ```javascript chart.setInterval("1h"); chart.setInterval("5m"); ``` **Parameters:** - `newInterval` (string): Time interval ('1m', '5m', '15m', '1h', '4h', '1D', etc.) #### `setTheme(newTheme)` Changes the chart theme. ```javascript chart.setTheme("dark"); chart.setTheme("light"); ``` **Parameters:** - `newTheme` (string): Theme name ('dark' | 'light') #### `setChartType(chartType)` Changes the chart type (candlestick, line, area, bar, etc.) of the currently selected chart. ```javascript chart.setChartType("line"); ``` **Parameters:** - `chartType` (string): The chart type to apply #### `resize(width, height)` Resizes the outer chart container directly (numbers are treated as px, strings are passed through as CSS values e.g. `"100%"`). The existing `AutoFit` `ResizeObserver` picks up the change and recalculates dimensions on the next render — you do **not** need to call anything else after resizing. ```javascript chart.resize(800, 600); chart.resize("100%", "50vh"); ``` **Parameters:** - `width` (number | string): New container width (px if a number) - `height` (number | string): New container height (px if a number) See also the [AutoFit guide](../guides/autofit.md) if you'd rather let the container's own size drive the chart. #### `goToDate(date)` Jumps to a specific date on the chart. ```javascript chart.goToDate("2025-06-01"); chart.goToDate(new Date(2025, 5, 1)); chart.goToDate({ date1: "2025-06-01", date2: "2025-06-15" }); // date range ``` **Parameters:** - `date` (string | Date | `{ date1, date2 }`): Date to navigate to — a `Date` object, an ISO string, or a `{ date1, date2 }` object for a range #### `setTimezone(timezone)` Sets the chart timezone. ```javascript chart.setTimezone("America/New_York"); ``` **Parameters:** - `timezone` (string): IANA timezone name #### `getTimezonePresets()` Returns the available timezone presets. ```javascript const presets = chart.getTimezonePresets(); // [{ key, name, label }, ...] ``` **Returns:** Array of timezone presets with `key`, `name`, and `label` #### `getCurrentTimezone()` Returns the current timezone setting. ```javascript const tz = chart.getCurrentTimezone(); ``` **Returns:** `string` - Current timezone ### Indicators, Drawings & Objects #### `addIndicator(indicator)` Adds an indicator to the currently selected chart. ```javascript chart.addIndicator({ type: "EMA", id: "ema-20" }); ``` **Parameters:** - `indicator` (object): The indicator object with `type` and `id` properties > **Access control:** if construct-time `study_count_limit` is set, `addIndicator` (here, on `activeChart()`, and `*AtIndex`) throws once the pane already has that many studies. `studies_access` (whitelist/blacklist) is enforced earlier by excluding disallowed types from `getStudiesList()`. #### `addDrawing(drawing, chartId?)` Adds a drawing to the currently selected chart. ```javascript chart.addDrawing({ type: "TREND_LINE", options: { /* coordinates */ }, }); ``` **Parameters:** - `drawing` (object): The drawing object with type/name, options, and appearance properties - `chartId` (string, optional): The chart pane ID to add the drawing to (default: `"MAIN_CHART"`) #### `deleteObject(objectId, chartId?)` Deletes an object (drawing or indicator) from the currently selected chart. ```javascript chart.deleteObject("ema-20"); ``` **Parameters:** - `objectId` (string): The ID of the object to delete - `chartId` (string, optional): The chart pane ID to delete from (default: `"MAIN_CHART"`) #### `updateSettings(settings)` Updates chart settings for the currently selected chart. ```javascript chart.updateSettings({ timezone: "America/New_York", showGrid: true }); ``` **Parameters:** - `settings` (object): Settings object to update ### Chart State #### `getChartState()` Returns the complete chart state for the currently selected chart (symbol, interval, indicators, drawings, settings). Useful for persisting and restoring charts. ```javascript const state = chart.getChartState(); localStorage.setItem("myChart", JSON.stringify(state)); ``` **Returns:** `object | null` - The complete chart state, or `null` if unavailable #### `setChartState(newState)` Restores a complete chart state on the currently selected chart. ```javascript const state = JSON.parse(localStorage.getItem("myChart")); chart.setChartState(state); ``` **Parameters:** - `newState` (object): The chart state object to apply ### Multichart Methods (by index) In multichart layouts, every "current chart" method has an `AtIndex` variant targeting a specific chart by its 0-based index: | Method | Description | | ----------------------------------------------------- | ---------------------------------------------- | | `setChartSymbolAtIndex(newSymbol, chartIdx)` | Change symbol of the chart at `chartIdx` | | `setIntervalAtIndex(newInterval, chartIdx)` | Change interval of the chart at `chartIdx` | | `setChartTypeAtIndex(chartType, chartIdx)` | Change chart type of the chart at `chartIdx` | | `addIndicatorAtIndex(indicator, chartIdx)` | Add an indicator to the chart at `chartIdx` | | `addDrawingAtIndex(drawing, chartIdx, chartId?)` | Add a drawing to the chart at `chartIdx` | | `deleteObjectAtIndex(objectId, chartIdx, chartId?)` | Delete an object from the chart at `chartIdx` | | `updateSettingsAtIndex(settings, chartIdx)` | Update settings of the chart at `chartIdx` | | `getChartStateAtIndex(chartIdx)` | Get state of the chart at `chartIdx` | | `setChartStateAtIndex(newState, chartIdx)` | Set state of the chart at `chartIdx` | ```javascript // Change the second chart in a multichart layout chart.setChartSymbolAtIndex("NASDAQ:MSFT", 1); chart.setIntervalAtIndex("1h", 1); chart.addIndicatorAtIndex({ type: "RSI", id: "rsi-14" }, 1); ``` Use the `CHART_SELECTED` event (see [Events API](./events.md)) to track which chart the user has selected. ### Templates #### `saveTemplate(templateName)` Saves the current indicators as a named template. ```javascript const template = chart.saveTemplate("My Setup"); ``` **Parameters:** - `templateName` (string): Name for the template **Returns:** The created template object #### `applyTemplate(template)` Applies a template to the current chart (replaces all indicators). ```javascript chart.applyTemplate(template); ``` **Parameters:** - `template` (object): Template object with an indicators array #### `getTemplates()` Returns all saved templates. ```javascript const templates = chart.getTemplates(); ``` **Returns:** Array of template objects #### `deleteTemplate(templateId)` Deletes a template by ID. ```javascript chart.deleteTemplate(templateId); ``` **Parameters:** - `templateId` (string): ID of the template to delete ### Trading Data #### `setBrokerAccounts(data)` Sets trading account data (accounts, orders, trades, positions) for the chart. ```javascript chart.setBrokerAccounts({ accountList: [ { account_id: "ACCOUNT_001", currency: "USD", balance: 50000, }, ], orderBook: [ /* active orders */ ], tradeBook: [ /* executed trades */ ], positions: [ /* open positions */ ], }); ``` **Parameters:** - `data` (object): Broker account data — see the [Datafeed API](./datafeed.md#-trading-data-formats) for the exact `orderBook`/`tradeBook`/`positions` formats #### `updatePositions(positions)` Updates existing positions without a full `setBrokerAccounts` rebuild — merges partial updates (e.g. `pnlMultiplier`, `bid`, `ask`) into positions by key and redraws. Throws if the chart isn't ready yet (same lifecycle as `setBrokerAccounts`). ```javascript chart.updatePositions([ { key: "demo-BYBIT:FUTURE:BTCUSDT-POS_001", bid: 50100, ask: 50101 }, ]); ``` **Parameters:** - `positions` (array|object): Partial position updates keyed by position `key` ### Data / Connection #### `resubscribeAll(idToken?)` Resubscribes to all active subscriptions and refreshes OHLCV. Handles candles, real-time ticks, compare symbols, and LIPI indicators. **When to call:** 1. **Tab reactivation** — After the browser tab was hidden for a long time, WebSocket throughput is throttled and candles may form incorrectly. Call this when the tab becomes visible again. 2. **WebSocket reconnection** — After a network drop. 3. **Auth token refresh** — Pass the new token so subscriptions use updated credentials. ```javascript // Recommended: refetch bars when the user returns to the tab let hiddenAt = 0; const LONG_HIDDEN_MS = 30_000; document.addEventListener("visibilitychange", () => { if (document.hidden) { hiddenAt = Date.now(); return; } if (hiddenAt && Date.now() - hiddenAt < LONG_HIDDEN_MS) return; chart.resubscribeAll(); }); // After WebSocket reconnect websocket.onreconnect = () => { chart.resubscribeAll(freshIdToken); }; ``` **Parameters:** - `idToken` (string, optional): Authentication token for resubscription ### OI Profile (Futures/Options) #### `setOIProfile(metric)` Sets the OI Profile metric for futures/options charts. ```javascript chart.setOIProfile("oi"); ``` **Parameters:** - `metric` (string): OI metric to display (`'oi'`, `'volume'`, `'delta'`, `'gamma'`, `'theta'`, `'vega'`) #### `getOIProfileSettings()` Returns the current OI Profile settings. ```javascript const settings = chart.getOIProfileSettings(); ``` **Returns:** `object` - Current OI profile settings ## Events Lifecycle and trading callbacks (`onReady`, `onError`, `appCallback`) live on [`ChartConfig`](./configuration.md). Runtime subscriptions (`chart.subscribe(...)`, layout/persistence events, …) are documented under **[Events](./events.md)**. The façade `subscribe` / `unsubscribe` methods are listed above under Widget façade. ## Best Practices ### 1. Use onReady Callback The chart instance doesn't expose `symbol`, `interval`, or `isReady` properties. Instead, use the `onReady` callback in the configuration: ```javascript const chart = createChart("#chart", { symbol: "AAPL", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_KEY", onReady: (chartInstance) => { // Chart is ready - perform operations here console.log("Chart is ready!"); // You can now safely call methods chartInstance.setSymbol("MSFT"); }, }); ``` ### 2. Handle Errors Gracefully Use the `onError` callback in the configuration to handle errors: ```javascript const chart = createChart("#chart", { symbol: "AAPL", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_KEY", onError: (error) => { console.error("Chart error:", error); // Handle the error appropriately }, }); ``` ### 3. Clean Up Resources (Critical!) ```javascript // When component unmounts or page unloads chart.destroy(); ``` **Why this is critical:** - Prevents WebSocket subscription leaks - Avoids receiving data for destroyed charts - Prevents memory leaks and performance issues - Required when recreating charts with new symbols/intervals **Common scenarios:** ```javascript // React hook cleanup useEffect(() => { const chart = createChart("#chart", config); return () => chart.destroy(); }, []); // Chart recreation function recreateChart() { // Always destroy before creating new chart if (currentChart) { currentChart.destroy(); } currentChart = createChart("#chart", newConfig); } // Page unload window.addEventListener("beforeunload", () => { if (chart) { chart.destroy(); } }); ``` ### 4. Use Batch Updates ```javascript // Instead of multiple calls chart.setBrokerAccounts({ accountList: [...], orderBook: [...], tradeBook: [...], positions: [...] }); // For frequent lightweight updates (prices, PnL), prefer: chart.updatePositions(partialUpdates); ``` ## Related Documentation - **[Configuration](./configuration.md)** — `ChartConfig` options - **[Chart Widget](./chart-widget.md)** — convenience helpers (`setSymbol`, `*AtIndex`, …) - **[GoCharting React Component](./gocharting-component.md)** — declarative React API - **[Events](./events.md)** — `appCallback` and `subscribe` channels - **[Datafeed](./datafeed.md)** — custom data sources - **[Themes](./themes.md)** — theme / `themeColor` / CSS variables - **[Framework integrations](../guides/framework-integration.md)** — React, Next, Vue, … - **[Examples](../examples.md)** — working demos --- _For more examples and advanced usage, see the [tutorials section](../tutorials/quick-start.md)._ File: docs/api/configuration.md ========================================== # Configuration API Pass a [`ChartConfig`](./types.md#chartconfig) object to [`createChart()`](./chart.md) (or as props on [``](./gocharting-component.md)). Nested objects map to typed shapes such as [`TradingConfig`](./types.md#tradingconfig) and [`ExcludeOptions`](./types.md#excludeoptions). ## Quick example ```javascript import { createChart } from "@gocharting/chart-sdk"; const chart = createChart("#chart-container", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", theme: "dark", disableSearch: false, disableCompare: false, trading: { enableTrading: true, showOpenOrders: true, showPositions: true, supportStopOrders: true, supportStopLimitOrders: true, }, appCallback: (event) => { console.log(event.eventType, event.message); }, onReady: (chartInstance) => { console.log("ready", chartInstance); }, onError: (error) => { console.error(error); }, }); ``` ## `ChartConfig` at a glance Canonical field list: **[Types → ChartConfig](./types.md#chartconfig)**. Summary of applied options: | Group | Fields | Types | | ----- | ------ | ----- | | Required | `datafeed`, `symbol`, `interval`, `licenseKey` | [`Datafeed`](./datafeed.md) | | Display | `theme`, `themeColor`, `locale`, `autosize`, `width`, `height` | — | | Chrome flags | `disableSearch`, `disableCompare`, `hideDrawingToolBar`, `exclude` | [`ExcludeOptions`](./types.md#excludeoptions) | | Trading | `trading` | [`TradingConfig`](./types.md#tradingconfig) | | Construct features (P4) | `disabled_features`, `enabled_features`, `overrides`, `studies_overrides`, `settings_overrides`, `custom_css_url`, `custom_themes`, `timezone`, `toolbar_bg`, `time_frames`, `timeframe`, `favorites` / `favourite` | — | | Construct chrome | `loading_screen`, `custom_font_family`, `compare_symbols`, `symbol_search_symbols_types`, `custom_timezones`, `custom_translate_function`, `header_widget_buttons_mode`, `additional_symbol_info_fields` | — | | Construct hooks | `symbol_search_complete`, `save_chart_to_server_callback`, `suggested_chart_change_adapter`, `context_menu.items_processor`, `context_menu.renderer_factory` | — | | Persistence | `autoSave`, `auto_save_delay`, `save_load_adapter`, `load_last_chart`, `saved_data`, `snapshot_url`, `image_storage_adapter` | — | | Mobile | `isNativeApp`, `touchMode` | — | | Overrides | `defaultInitialChartConfig`, `contextMenu`, `popups`, `favourite` | [`DefaultInitialChartConfig`](./types.md#defaultinitialchartconfig), [`ContextMenuOptions`](./types.md#contextmenuoptions), [`PopupsConfig`](./types.md#popupsconfig), [`FavouriteConfig`](./types.md#favouriteconfig) | | Callbacks | `appCallback`, `onReady`, `onError` | [Events](./events.md) | > [!NOTE] > > Options that are accepted but not applied yet are omitted here (same policy as other API pages). Prefer the types file for the full TypeScript surface. --- ## Required options | Option | Type | Description | | ------ | ---- | ----------- | | `datafeed` | [`Datafeed`](./datafeed.md) | Bars, resolve, optional search / ticks / marks | | `symbol` | `string` | Initial symbol (e.g. `"BYBIT:FUTURE:BTCUSDT"`) | | `interval` | `string` | Initial interval (e.g. `"1m"`, `"1D"`) | | `licenseKey` | `string` | SDK license key (unless `skipLicenseValidation: true`) | --- ## Display ### `theme` / `themeColor` - **`theme`:** `"light"` \| `"dark"` (default `"light"`). Maps to `#ffffff` / `#22292f` when `themeColor` is omitted. - **`themeColor`:** any hex background (e.g. `"#570f0f"`). You may pass hex alone — do not also force `theme: "light"` unless you want the named mode. ```javascript theme: "dark", // or themeColor: "#1a1a1a", ``` See [Themes](./themes.md). Interactive lab: [`examples/themes-lab.html`](../../examples/themes-lab.html). ### `autosize` / `width` / `height` / `locale` | Option | Default | Notes | | ------ | ------- | ----- | | `autosize` | `true` | AutoFit responsive sizing | | `width` / `height` | `"100%"` | Number (px) or CSS length | | `locale` | `"en-US"` | UI translation locale | --- ## Chrome flags ### `disableSearch` / `disableCompare` / `hideDrawingToolBar` | Option | Default | Effect | | ------ | ------- | ------ | | `disableSearch` | `false` | Hide top-bar symbol search | | `disableCompare` | `false` | Hide Compare control | | `hideDrawingToolBar` | `false` | Hide left drawing toolbar | ```javascript disableSearch: true, disableCompare: true, hideDrawingToolBar: true, ``` ### `exclude` **Type:** [`ExcludeOptions`](./types.md#excludeoptions) Hide chrome regions or indicators at construct time: ```javascript exclude: { leftPanel: true, rightPanel: true, drawingToolbar: true, indicators: ["SMA", "EMA"], }, ``` Runtime toggles also exist via `chart.features()` — see [Chart API](./chart.md). --- ## Trading — `trading` **Type:** [`TradingConfig`](./types.md#tradingconfig) ```javascript trading: { enableTrading: true, showOpenOrders: true, showPositions: true, showExecutions: true, showNotifications: true, showReverseButton: true, beep: true, quickTradeMode: false, supportStopOrders: true, supportStopLimitOrders: true, enableTakeProfitDefaults: true, defaultTakeProfitSpread: 10, defaultTakeProfitSpreadType: "tick", enableStopLossDefaults: true, defaultStopLossSpread: 5, defaultStopLossSpreadType: "tick", boxAlignment: "right", lineCategory: "extended", }, ``` Notes: - Gate is `trading.enableTrading` (default `false`). - Orders/positions need broker data via `setBrokerAccounts()` — see [Trading](./trading.md). - Handle UI actions with [`appCallback`](./events.md) (`PLACE_ORDER`, `MODIFY_ORDER`, …). - `supportStopOrders` / `supportStopLimitOrders` also require broker `orderConfig` support for stop types. Minimal enable: ```javascript trading: { enableTrading: true }, ``` --- ## Construct-time features (Phase 4) Apply once at `createChart`. Prefer these for initial chrome; use runtime `features()` / `applyOverrides()` for later changes. ### `disabled_features` / `enabled_features` TV-style featuresets mapped onto GC chrome (`exclude`, `disableSearch`, `disableCompare`, `hideDrawingToolBar`, …). `enabled_features` runs after `disabled_features`. ```javascript disabled_features: ["header_symbol_search", "left_toolbar", "header_compare"], enabled_features: ["header_compare"], // re-enable after disable ``` Lab: [`examples/phase4-constructor-lab.html`](../../examples/phase4-constructor-lab.html). ### `overrides` / `studies_overrides` / `settings_overrides` | Option | Merges into | | ------ | ----------- | | `overrides` | Chart appearance (same nested GC shape as `applyOverrides`) | | `studies_overrides` | Study styles keyed by indicator type | | `settings_overrides` | `defaultInitialChartConfig.settings` | ```javascript overrides: { appearance: { backgroundColor: "#0f1419" }, }, studies_overrides: { EMA: { appearance: { strokeStyle: "#3d8bfd" } }, }, settings_overrides: { zone: "America/New_York", }, ``` ### `custom_css_url` / `custom_themes` / `toolbar_bg` / `timezone` | Option | Effect | | ------ | ------ | | `custom_css_url` | Inject stylesheet once (`addCustomCSSFile`) | | `custom_themes` | CSS theme tokens via `customThemes()` | | `toolbar_bg` | Sets toolbar CSS vars on the chart container | | `timezone` | IANA zone → `settings.zone` | ### Construct chrome | Option | Effect | | ------ | ------ | | `loading_screen` | Overlay while hydrating — `{ backgroundColor?, foregroundColor?, text? }` | | `custom_font_family` | CSS font family on the chart container | | `compare_symbols` | Seed Compare overlays (string or `{ symbol, exchange?, … }` entries) | | `symbol_search_symbols_types` | Type filter chips in symbol search (`string` or `{ name, value }` entries) | | `custom_timezones` | Extra zones merged into the timezone picker | | `custom_translate_function` | `(key, options, defaultMessage?) => string` wrap after intl init | | `header_widget_buttons_mode` | `"fullsize"` \| `"compact"` \| `"adaptive"` top-bar density | | `additional_symbol_info_fields` | Extra fields shown with symbol status / bottom bar | > [!NOTE] > Chart **Y-axis tick labels** use symbol `tick_size` / `max_tick_precision` from `resolveSymbol` — not a host-controlled TV `pricescale`. Construct `numeric_formatting` / `custom_formatters` (if present on the type surface) only affect optional host helpers and do **not** restyle the live price axis. ```javascript loading_screen: { backgroundColor: "#0f1419", foregroundColor: "#fff", text: "Loading…" }, custom_font_family: "IBM Plex Sans, sans-serif", compare_symbols: ["NASDAQ:AAPL", { symbol: "MSFT", exchange: "NASDAQ" }], symbol_search_symbols_types: ["stock", { name: "Crypto", value: "crypto" }], header_widget_buttons_mode: "compact", ``` ### Construct hooks | Option | Effect | | ------ | ------ | | `symbol_search_complete` | Called after a successful symbol pick from search | | `save_chart_to_server_callback` | Called when a named layout save completes | | `suggested_chart_change_adapter` | `{ canAcceptSuggestedChange, onSuggestedChange? }` — gate `setSymbol` / `setInterval` | | `context_menu.items_processor` | Post-process context-menu items before show | | `context_menu.renderer_factory` | Replace the item list (not full custom React trees) | ```javascript symbol_search_complete: (symbol) => console.log("picked", symbol), save_chart_to_server_callback: (meta) => console.log("saved", meta), suggested_chart_change_adapter: { canAcceptSuggestedChange: ({ type }) => type !== "symbol" || confirm("Change symbol?"), }, context_menu: { items_processor: (items) => items.filter((i) => i.label !== "Hide"), }, ``` ### `time_frames` / `timeframe` / `favorites` | Option | Effect | | ------ | ------ | | `time_frames` | Filter intervals exposed by `getIntervals` / picker | | `timeframe` | After init, `activeChart().setTimeFrame(...)` | | `favorites` | TV-shaped favorites mapped onto GC [`FavouriteConfig`](./types.md#favouriteconfig) | --- ## Persistence Two different mechanisms: | Option | What it does | | ------ | ------------ | | `autoSave` | Persist chart UI state to **`sessionStorage`** (tab-scoped; default `true`) | | `auto_save_delay` | Debounce (seconds, default `5`) before widget event **`onAutoSaveNeeded`** — host should call `saveChartToServer` / your adapter | ```javascript autoSave: false, // you own layout state auto_save_delay: 2, save_load_adapter: mySaveLoadAdapter, load_last_chart: true, // or saved_data: layoutBlob, // wins over load_last_chart snapshot_url: "https://example.com/upload-screenshot", ``` | Option | Notes | | ------ | ----- | | `save_load_adapter` | Named chart / study-template persistence (Phase 5) | | `load_last_chart` | After init, load newest chart from adapter | | `saved_data` | Layout blob (or JSON string) applied after hydrate | | `snapshot_url` | POST target for `takeScreenshot()` (multipart `preparedImage`) | | `image_storage_adapter` | Preferred over `snapshot_url` when both are set | | `client_id` / `user_id` | Optional storage scoping | Subscribe: `chart.subscribe("onAutoSaveNeeded", …)` — see [Events](./events.md). Lab: [`examples/phase5-persistence-lab.html`](../../examples/phase5-persistence-lab.html). --- ## Mobile / WebView | Option | Default | Behavior | | ------ | ------- | -------- | | `isNativeApp` | `false` | Mobile canvas; **hide** JS top/bottom bars; suppress JS context menu → `OPEN_CONTEXT_MENU`; bridge via `sendToNative` | | `touchMode` | `false` | Full **JS** mobile Chart-tab chrome (bars + menus), not the full TerminalMobile app | ```javascript isNativeApp: true, appCallback: (event) => { // Single event object — also posted to the native bridge console.log(event.eventType, event.message); }, ``` Guides: [Mobile WebView](../guides/mobile-integration.md) (includes a Desktop/Mobile **touchMode true vs false** compare lab). --- ## Nested overrides ### `defaultInitialChartConfig` **Type:** [`DefaultInitialChartConfig`](./types.md#defaultinitialchartconfig) Deep-merge into initial chart settings / appearance: ```javascript defaultInitialChartConfig: { settings: { showCrosshairPlusIcon: false, zone: "America/New_York", }, }, ``` `showCrosshairPlusIcon` at the top level of `ChartConfig` is equivalent to `defaultInitialChartConfig.settings.showCrosshairPlusIcon`. ### `contextMenu` **Type:** [`ContextMenuOptions`](./types.md#contextmenuoptions) Alias: `context_menu`. ```javascript contextMenu: { showTradingOptions: true }, ``` ### `popups` **Type:** [`PopupsConfig`](./types.md#popupsconfig) Panel state overrides keyed by panel id (`TradingPanel`, `Settings`, `Layers`, …) — **not** welcome/tutorial flags. ```javascript popups: { TradingPanel: { title: "Order Ticket" }, Settings: { visible: false }, }, ``` ### `favourite` **Type:** [`FavouriteConfig`](./types.md#favouriteconfig) ```javascript favourite: { INTERVALS: ["1m", "5m", "1D"], DRAWINGS: ["trendline", "rectangle"], INDICATOR: ["RSI", "MACD"], }, ``` --- ## Callbacks & misc flags | Option | Default | Notes | | ------ | ------- | ----- | | `appCallback` | — | Single [`AppCallbackEvent`](./events.md) object — see [Events](./events.md) | | `onReady` | — | Chart instance ready for API calls | | `onError` | — | Construct / fatal errors | | `debugLog` | `false` | Verbose SDK console logs | | `skipLicenseValidation` | `false` | Demo only — skip `licenseKey` requirement | | `alwaysDrawMode` | `false` | Keep drawing tool armed after place | --- ## Related - **[Type Definitions](./types.md#chartconfig)** — `ChartConfig`, `TradingConfig`, and nested shapes - **[Chart API](./chart.md)** — runtime methods (`applyOverrides`, `features`, save/load) - **[Events](./events.md)** — `appCallback`, `onAutoSaveNeeded`, `subscribe` - **[Themes](./themes.md)** — `theme` / `themeColor` / `setTheme` - **[Trading](./trading.md)** — broker book + order events - **[Framework integrations](Framework integrations (nextra))** — sample apps Labs: [`phase4-constructor-lab.html`](../../examples/phase4-constructor-lab.html), [`tv-parity-construct-lab.html`](../../examples/tv-parity-construct-lab.html), [`phase5-persistence-lab.html`](../../examples/phase5-persistence-lab.html), [`themes-lab.html`](../../examples/themes-lab.html). File: docs/api/custom-feed.md ======================================== # Custom Datafeed Implementation Guide This guide shows you how to implement a custom datafeed to connect the GoCharting SDK to your own data source. ## Overview A datafeed is a plain JavaScript object (no class inheritance required) that implements the `Datafeed` interface and is passed as `datafeed` to `createChart()` or the `GoCharting` component. It has **2 required methods** and **5 optional methods**: **Required:** - `getBars()` - Fetch historical bar data - `resolveSymbol()` - Resolve symbol information **Optional:** - `searchSymbols()` - Enable symbol search - `subscribeTicks()` - Provide real-time updates - `unsubscribeTicks()` - Cancel real-time subscriptions - `getMarks()` - Display marks/events on chart - `getTimescaleMarks()` - Display timescale marks ```javascript const myDatafeed = { // Required async getBars(symbolInfo, resolution, periodParams) { /* ... */ }, resolveSymbol(symbolName, onResolve, onError) { /* ... */ }, // Optional searchSymbols(userInput, exchange, symbolType, onResultReadyCallback) { /* ... */ }, subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) { /* ... */ }, unsubscribeTicks(subscriberUID) { /* ... */ }, getMarks(symbolInfo, startDate, endDate, onDataCallback, resolution) { /* ... */ }, getTimescaleMarks(symbolInfo, from, to, onDataCallback, resolution) { /* ... */ }, }; ``` See the [Datafeed API](./datafeed.md) for complete interface documentation. ## Quick Start ### Minimal Datafeed Here's the simplest possible datafeed implementation: ```javascript const myDatafeed = { // Required: Fetch historical bars async getBars(symbolInfo, resolution, periodParams) { const { from, to } = periodParams; // Fetch from your API const response = await fetch( `/api/bars?symbol=${symbolInfo.symbol}&from=${from.getTime()}&to=${to.getTime()}` ); const data = await response.json(); // Return in BarsResult format return { bars: data.map(bar => ({ time: bar.timestamp, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })), }; }, // Required: Resolve symbol information resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "NASDAQ"; const segment = parts.length >= 3 ? parts[1] : "EQUITY"; const symbol = parts[parts.length - 1] || "AAPL"; const name = symbol; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name, asset_type: "EQUITY", source_id: symbol, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.01, display_tick_size: 0.01, volume_size_increment: 1, max_tick_precision: 2, max_volume_precision: 0, quote_currency: "USD", supports: { footprint: false }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "America/New_York", hours: [ { open: false }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: false }, ], valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: "stock", session: "0930-1600", timezone: "America/New_York", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); }, }; ``` ### Using Your Datafeed ```javascript import { createChart } from "@gocharting/chart-sdk"; const chart = createChart("#chart", { symbol: "AAPL", interval: "1D", datafeed: myDatafeed, // Your custom datafeed licenseKey: "YOUR_LICENSE_KEY", }); ``` ## Complete Implementation Examples ### Example 1: REST API Datafeed ```javascript class RestAPIDatafeed { constructor(apiBaseUrl) { this.apiBaseUrl = apiBaseUrl; } async getBars(symbolInfo, resolution, periodParams) { const { from, to, firstDataRequest } = periodParams; try { const response = await fetch( `${this.apiBaseUrl}/bars?` + `symbol=${symbolInfo.symbol}&` + `interval=${resolution}&` + `from=${from.getTime()}&` + `to=${to.getTime()}` ); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); return { bars: data.bars.map(bar => ({ time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })), meta: { noData: data.bars.length === 0, }, }; } catch (error) { console.error("getBars error:", error); return { bars: [], meta: { noData: true } }; } } resolveSymbol(symbolName, onResolve, onError) { fetch(`${this.apiBaseUrl}/symbols/${symbolName}`) .then(response => response.json()) .then(data => { onResolve({ ...data, // full GoCharting security from API ticker: data.ticker || data.symbol, full_name: data.full_name || `${data.exchange}:${data.segment}:${data.symbol}`, description: data.description || data.name, type: (data.type || (data.asset_type || "EQUITY").toLowerCase()), }); }) .catch(error => { console.error("resolveSymbol error:", error); onError("Symbol not found"); }); } // Optional: Enable symbol search searchSymbols(userInput, exchange, symbolType, onResultReadyCallback) { fetch( `${this.apiBaseUrl}/search?` + `query=${encodeURIComponent(userInput)}&` + `exchange=${exchange}&` + `type=${symbolType}` ) .then(response => response.json()) .then(data => { const results = data.map(item => ({ symbol: item.symbol, full_name: item.full_name, description: item.description, exchange: item.exchange, type: item.type, })); onResultReadyCallback({ searchInProgress: false, items: results }); }) .catch(error => { console.error("searchSymbols error:", error); onResultReadyCallback({ searchInProgress: false, items: [] }); }); } } // Usage const datafeed = new RestAPIDatafeed("https://api.example.com"); const chart = createChart("#chart", { symbol: "AAPL", interval: "1D", datafeed: datafeed, licenseKey: "YOUR_LICENSE_KEY", }); ``` ### Example 2: WebSocket Datafeed with Real-time Updates ```javascript class WebSocketDatafeed { constructor(apiBaseUrl, wsUrl) { this.apiBaseUrl = apiBaseUrl; this.wsUrl = wsUrl; this.ws = null; this.subscribers = new Map(); // subscriberUID -> callback } async getBars(symbolInfo, resolution, periodParams) { const { from, to } = periodParams; const response = await fetch( `${this.apiBaseUrl}/bars?` + `symbol=${symbolInfo.symbol}&` + `interval=${resolution}&` + `from=${from.getTime()}&` + `to=${to.getTime()}` ); const data = await response.json(); return { bars: data.map(bar => ({ time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })), }; } resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "BYBIT"; const segment = parts.length >= 3 ? parts[1] : "FUTURE"; const symbol = parts[parts.length - 1] || "BTCUSDT"; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name: "BTC / USDT PERPETUAL FUTURES", asset_type: "CRYPTO", source_id: symbol, pair: { from: "BTC", to: "USDT" }, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.1, display_tick_size: 1, volume_size_increment: 0.001, max_tick_precision: 1, max_volume_precision: 3, quote_currency: "USDT", future_type: "PERP", supports: { footprint: true }, exchange_info: { name: exchange.toLowerCase(), code: exchange, zone: "UTC", hours: Array.from({ length: 7 }, () => ({ open: true })), valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: "BTC / USDT PERPETUAL FUTURES", type: "crypto", session: "24x7", timezone: "UTC", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); } // Optional: Subscribe to real-time updates subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) { // Store callback this.subscribers.set(subscriberUID, onRealtimeCallback); // Connect WebSocket if not already connected if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.ws = new WebSocket(this.wsUrl); this.ws.onopen = () => { console.log("WebSocket connected"); // Subscribe to symbol this.ws.send(JSON.stringify({ type: "subscribe", symbol: symbolInfo.symbol, interval: resolution, })); }; this.ws.onmessage = (event) => { const data = JSON.parse(event.data); // Send update to all subscribers this.subscribers.forEach(callback => { callback({ time: data.time, open: data.open, high: data.high, low: data.low, close: data.close, volume: data.volume, }); }); }; this.ws.onerror = (error) => { console.error("WebSocket error:", error); }; this.ws.onclose = () => { console.log("WebSocket disconnected"); }; } } // Optional: Unsubscribe from real-time updates unsubscribeTicks(subscriberUID) { this.subscribers.delete(subscriberUID); // Close WebSocket if no more subscribers if (this.subscribers.size === 0 && this.ws) { this.ws.close(); this.ws = null; } } // Optional: Cleanup destroy() { if (this.ws) { this.ws.close(); this.ws = null; } this.subscribers.clear(); } } // Usage const datafeed = new WebSocketDatafeed( "https://api.example.com", "wss://ws.example.com" ); const chart = createChart("#chart", { symbol: "BTCUSDT", interval: "1m", datafeed: datafeed, licenseKey: "YOUR_LICENSE_KEY", }); ``` ### Example 3: Cached Datafeed ```javascript class CachedDatafeed { constructor(apiBaseUrl) { this.apiBaseUrl = apiBaseUrl; this.cache = new Map(); // symbol+interval -> bars } getCacheKey(symbol, interval) { return `${symbol}_${interval}`; } async getBars(symbolInfo, resolution, periodParams) { const { from, to, firstDataRequest } = periodParams; const cacheKey = this.getCacheKey(symbolInfo.symbol, resolution); // Check cache for first request if (firstDataRequest && this.cache.has(cacheKey)) { const cachedBars = this.cache.get(cacheKey); const filteredBars = cachedBars.filter( bar => bar.time >= from.getTime() && bar.time <= to.getTime() ); if (filteredBars.length > 0) { console.log("Using cached data"); return { bars: filteredBars }; } } // Fetch from API const response = await fetch( `${this.apiBaseUrl}/bars?` + `symbol=${symbolInfo.symbol}&` + `interval=${resolution}&` + `from=${from.getTime()}&` + `to=${to.getTime()}` ); const data = await response.json(); const bars = data.map(bar => ({ time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })); // Update cache if (firstDataRequest) { this.cache.set(cacheKey, bars); } return { bars }; } resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "NASDAQ"; const segment = parts.length >= 3 ? parts[1] : "EQUITY"; const symbol = parts[parts.length - 1] || "AAPL"; const name = symbol; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name, asset_type: "EQUITY", source_id: symbol, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.01, display_tick_size: 0.01, volume_size_increment: 1, max_tick_precision: 2, max_volume_precision: 0, quote_currency: "USD", supports: { footprint: false }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "America/New_York", hours: [ { open: false }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: false }, ], valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: "stock", session: "0930-1600", timezone: "America/New_York", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); } // Clear cache when needed clearCache() { this.cache.clear(); } } // Usage const datafeed = new CachedDatafeed("https://api.example.com"); const chart = createChart("#chart", { symbol: "AAPL", interval: "1D", datafeed: datafeed, licenseKey: "YOUR_LICENSE_KEY", }); ``` ## Implementation Tips ### 1. Error Handling Always handle errors gracefully in your datafeed methods: ```javascript async getBars(symbolInfo, resolution, periodParams) { try { const response = await fetch(/* ... */); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); return { bars: data }; } catch (error) { console.error("getBars error:", error); // Return empty bars with noData flag return { bars: [], meta: { noData: true } }; } } ``` ### 2. Resolution Conversion Convert SDK resolution format to your API format: ```javascript function convertResolution(resolution) { // SDK uses: "1m", "5m", "15m", "1h", "1D", etc. // Your API might use: "1", "5", "15", "60", "D", etc. if (typeof resolution === "string") { if (resolution.endsWith("m")) { return resolution.slice(0, -1); // "1m" -> "1" } if (resolution.endsWith("h")) { return String(parseInt(resolution) * 60); // "1h" -> "60" } if (resolution.endsWith("D")) { return "D"; // "1D" -> "D" } } return resolution; } ``` ### 3. Timestamp Handling Ensure timestamps are in the correct format: ```javascript // SDK expects timestamps in milliseconds const bars = data.map(bar => ({ time: bar.timestamp * 1000, // Convert seconds to milliseconds open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })); ``` ### 4. Symbol Information Provide accurate symbol information: ```javascript resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "NASDAQ"; const segment = parts.length >= 3 ? parts[1] : "EQUITY"; const symbol = parts[parts.length - 1] || "AAPL"; const name = symbol; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name, asset_type: "EQUITY", source_id: symbol, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.01, display_tick_size: 0.01, volume_size_increment: 1, max_tick_precision: 2, max_volume_precision: 0, quote_currency: "USD", supports: { footprint: false }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "America/New_York", hours: [ { open: false }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: false }, ], valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: "stock", session: "0930-1600", timezone: "America/New_York", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); } ``` ## Datafeed Checklist ### Required Methods - `getBars()` - Fetch historical bars - Handle `from` and `to` dates correctly - Return bars in correct format (BarsResult or UDF) - Handle errors gracefully - Return `noData: true` when no data available - `resolveSymbol()` - Resolve symbol info - Call `onResolve()` with symbol information - Call `onError()` if symbol not found - Provide accurate `max_tick_precision` (price scale is derived as `10^max_tick_precision`) - List all `supported_resolutions` ### Optional Methods (Recommended) - `searchSymbols()` - Enable symbol search - Return array of search results - Include `symbol`, `full_name`, `description`, `exchange`, `type` - `subscribeTicks()` - Real-time updates - Connect to WebSocket or polling mechanism - Call `onRealtimeCallback()` with new bars/ticks - Handle connection errors - `unsubscribeTicks()` - Cancel subscriptions - Clean up WebSocket connections - Remove event listeners ## Related Documentation - **[Datafeed API](./datafeed.md)** - Complete Datafeed interface reference - **[Helper Functions](./helpers.md)** - Utility functions like `getDateRangeForDuration()` - **[TypeScript Types](./types.md)** - Type definitions for Bar, SymbolInfo, etc. - **[Examples](../examples/)** - Working code examples --- _For complete working examples, see the [Examples](../examples/) section._ File: docs/api/datafeed.md ===================================== # Datafeed API The Datafeed API defines the interface for implementing custom data sources in the GoCharting SDK. It provides methods for fetching market data, resolving symbols, and providing real-time updates. ## Datafeed Object ```javascript // Create a simple datafeed object (no inheritance needed) const myDatafeed = { // Implement required methods async getBars(symbolInfo, resolution, periodParams) { // Your implementation return { bars }; }, resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "NASDAQ"; const segment = parts.length >= 3 ? parts[1] : "EQUITY"; const symbol = parts[parts.length - 1] || "AAPL"; const name = symbol; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name, asset_type: "EQUITY", source_id: symbol, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.01, display_tick_size: 0.01, volume_size_increment: 1, max_tick_precision: 2, max_volume_precision: 0, quote_currency: "USD", supports: { footprint: false }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "America/New_York", hours: [ { open: false }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: false }, ], valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: "stock", session: "0930-1600", timezone: "America/New_York", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); }, }; ``` ## Required Methods ### `getBars(symbolInfo, resolution, periodParams)` Fetches historical bar data for the specified symbol and time range. **Parameters:** - `symbolInfo` - Symbol information object (see SymbolInfo format below) - `resolution` - Resolution object with `{scale, units}` format - `periodParams` - Object with `{from, to, firstDataRequest, rows}` properties **Returns:** Array of bar objects in UDF format ```javascript async getBars(symbolInfo, resolution, periodParams) { const { from, to, firstDataRequest, rows } = periodParams; const { scale, units } = resolution; // Convert resolution to exchange format const interval = getExchangeInterval(scale, units); // Fetch data from your API const response = await fetch(`/api/bars?symbol=${symbolInfo.symbol}&interval=${interval}&from=${from.getTime()}&to=${to.getTime()}`); const data = await response.json(); // Return bars in UDF format return { s: "ok", // Status: "ok" | "no_data" | "error" t: data.map(bar => Math.floor(bar.timestamp / 1000)), // Unix timestamps in seconds o: data.map(bar => bar.open), // Open prices h: data.map(bar => bar.high), // High prices l: data.map(bar => bar.low), // Low prices c: data.map(bar => bar.close), // Close prices v: data.map(bar => bar.volume) // Volumes }; } // Helper function to convert resolution function getExchangeInterval(scale, units) { switch (scale) { case "minutes": return units; // 1, 5, 15, 30 case "hours": return 60 * units; // 60, 120, 240 case "days": return "D"; // Daily case "weeks": return "W"; // Weekly case "months": return "M"; // Monthly default: return 60; // Default 1 hour } } ``` **Parameters:** - `symbolInfo` (object): Symbol information from `resolveSymbol` - `resolution` (string): Time interval ('1', '5', '15', '60', '1D', etc.) - `periodParams` (object): Time range and request parameters ### `getMarks(symbolInfo, startDate, endDate, onDataCallback, resolution)` _(Optional)_ Fetches marks (events) for the specified symbol and time range. Marks appear as colored circles on the chart with hover tooltips. **Parameters:** - `symbolInfo` (object): Symbol information from `resolveSymbol` - `startDate` (number): Start time as Unix timestamp in seconds - `endDate` (number): End time as Unix timestamp in seconds - `onDataCallback` (function): Callback to deliver the marks array - `resolution` (object): Resolution object with `{scale, units}` format **Implementation Example:** ```javascript async getMarks(symbolInfo, startDate, endDate, onDataCallback, resolution) { // Fetch marks from your API const response = await fetch(`/api/timemarks?symbol=${symbolInfo.symbol}&from=${startDate}&to=${endDate}`); const events = await response.json(); // Deliver marks via the callback onDataCallback(events.map(event => ({ id: event.id, // Unique identifier (string|number) time: event.timestamp, // Unix timestamp in seconds color: event.color || 'blue', // CSS color for circle text: event.description, // Tooltip text (string or array) label: event.label || 'E', // Single character for circle labelFontColor: event.labelColor || 'white', // Label text color minSize: event.size || 25 // Minimum circle size in pixels }))); } ``` The SDK also supports an optional `getTimescaleMarks(symbolInfo, from, to, onDataCallback, resolution)` method with the same callback pattern for marks rendered on the timescale. **Real-world Examples:** ```javascript // Example 1: Earnings Report { id: 'earnings_2024_q1', time: 1704067200, // January 1, 2024 color: 'green', text: ['Q1 Earnings', 'EPS: $2.45', 'Revenue: $12.3B'], label: 'E', labelFontColor: 'white', minSize: 30 } // Example 2: News Event { id: 'news_merger_announcement', time: 1704153600, // January 2, 2024 color: '#ff6b35', text: 'Major acquisition announced', label: 'N', labelFontColor: 'white', minSize: 25 } // Example 3: Dividend Payment { id: 'dividend_2024_q1', time: 1704240000, // January 3, 2024 color: 'purple', text: ['Dividend Payment', '$0.85 per share', 'Ex-date: Jan 3, 2024'], label: 'D', labelFontColor: 'yellow', minSize: 28 } ``` **Return Format Specification:** Each time mark object must contain these properties: | Property | Type | Required | Description | | ---------------- | -------------- | -------- | ---------------------------------------------------------------------- | | `id` | string\|number | | Unique identifier for the mark | | `time` | number | | Unix timestamp in seconds | | `color` | string | | CSS color for the mark circle (e.g., 'red', '#ff0000', 'rgb(255,0,0)') | | `text` | string\|array | | Tooltip content. Arrays display each item on separate lines | | `label` | string | | Single character to display in the circle | | `labelFontColor` | string | | Color for the label text (default: 'white') | | `minSize` | number | | Minimum circle size in pixels (default: 25) | **Supported Text Formats:** ```javascript // Single line tooltip text: "Earnings announcement"; // Multi-line tooltip (recommended for detailed events) text: [ "Q4 Earnings Report", "EPS: $3.21 (Beat by $0.15)", "Revenue: $15.2B (Up 12% YoY)", "Guidance: Raised for 2024", ]; // HTML is not supported - use plain text only ``` **Color Examples:** ```javascript // Named colors color: "red"; color: "blue"; color: "green"; color: "purple"; color: "orange"; // Hex colors color: "#ff0000"; // Red color: "#00ff00"; // Green color: "#0000ff"; // Blue color: "#ff6b35"; // Orange // RGB colors color: "rgb(255, 0, 0)"; // Red color: "rgba(255, 0, 0, 0.8)"; // Semi-transparent red ``` ### `resolveSymbol(symbolName, onResolve, onError)` Resolves a symbol name to detailed symbol information. > ** Symbol Naming Convention**: The `symbol` field must **NOT** contain special characters such as `/`, `\`, `:`, `*`, `?`, `"`, `<`, `>`, or `|`. Use only alphanumeric characters (e.g., `EURUSD` instead of `EUR/USD`, `BTCUSDT` instead of `BTC/USDT`). Human-readable names with special characters can be placed in the `name` or `description` field. ```javascript async resolveSymbol(symbolName, onResolve, onError) { try { // Parse symbol name (e.g., "BYBIT:FUTURE:BTCUSDT") const [exchange, segment, symbol] = symbolName.split(':'); // Fetch symbol info from your API const response = await fetch(`/api/symbols/${exchange}/${segment}/${symbol}`); const symbolData = await response.json(); // The API returns a complete symbolInfo object with all required fields // You can use it directly or map specific fields as needed const symbolInfo = { // Basic Information exchange: symbolData.exchange, segment: symbolData.segment, symbol: symbolData.symbol, name: symbolData.name, asset_type: symbolData.asset_type, source_id: symbolData.source_id, // Pair Information (for crypto/forex) pair: symbolData.pair, // Trading Information tradeable: symbolData.tradeable, is_index: symbolData.is_index, is_formula: symbolData.is_formula, // Data Feed Information delay_seconds: symbolData.delay_seconds, data_status: symbolData.data_status, data_source_location: symbolData.data_source_location, // Display Information industry: symbolData.industry, symbol_logo_urls: symbolData.symbol_logo_urls, // Price & Volume Precision contract_size: symbolData.contract_size, tick_size: symbolData.tick_size, display_tick_size: symbolData.display_tick_size, volume_size_increment: symbolData.volume_size_increment, max_tick_precision: symbolData.max_tick_precision, max_volume_precision: symbolData.max_volume_precision, quote_currency: symbolData.quote_currency, // Futures-specific future_type: symbolData.future_type, // Feature Support supports: symbolData.supports, // Exchange Information exchange_info: symbolData.exchange_info, // Legacy/Compatibility Fields ticker: symbolData.symbol, full_name: `${symbolData.exchange}:${symbolData.segment}:${symbolData.symbol}`, description: symbolData.name, type: symbolData.asset_type?.toLowerCase() || 'crypto', session: symbolData.exchange_info?.hours?.every(h => h.open) ? '24x7' : '0930-1600', timezone: symbolData.exchange_info?.zone || 'UTC', has_intraday: true, has_daily: true, supported_resolutions: symbolData.exchange_info?.valid_intervals || ['1', '5', '15', '30', '60', '1D'], volume_precision: symbolData.max_volume_precision }; onResolve(symbolInfo); } catch (error) { onError('Symbol not found'); } } ``` **Parameters:** - `symbolName` (string): Symbol to resolve (e.g., "NASDAQ:AAPL") - `onResolve` (function): Callback for successful resolution - `onError` (function): Callback for errors ## Optional Methods ### `onReady(callback)` _(Optional but recommended)_ TV-compatible configuration handshake. Called once when the chart initializes. Pass a `DatafeedConfiguration` to the callback (prefer asynchronously). ```javascript onReady(callback) { setTimeout(() => { callback({ exchanges: [ { value: "", name: "All Exchanges", desc: "" }, { value: "NASDAQ", name: "NASDAQ", desc: "NASDAQ" }, ], symbols_types: [ { name: "All types", value: "" }, { name: "Stock", value: "stock" }, ], supported_resolutions: ["1", "5", "15", "60", "1D", "1W"], supports_search: true, supports_group_request: false, supports_marks: false, supports_timescale_marks: false, supports_time: false, }); }, 0); } ``` Read the cached result via `chart.getDatafeedConfiguration()`. When the active symbol has no `valid_intervals`, `chart.getIntervals()` uses `supported_resolutions` (TV or GC strings). Lab: [`phase6-onReady-udf-lab.html`](../../examples/phase6-onReady-udf-lab.html). ### `searchSymbols(userInput, exchange, symbolType, onResultReadyCallback)` _(Optional)_ Searches for symbols based on user input. If not implemented, symbol search functionality will be disabled in the chart. ```javascript async searchSymbols(userInput, exchange, symbolType, onResultReadyCallback) { try { const response = await fetch(`/api/search?q=${encodeURIComponent(userInput)}`); const results = await response.json(); const symbols = results.map(item => ({ symbol: item.symbol, full_name: item.full_name, description: item.description, exchange: item.exchange, ticker: item.ticker, type: item.type, key: item.key || `${item.exchange}:${item.segment || 'FUTURE'}:${item.symbol}` // REQUIRED: Unique key for symbol resolution })); onResultReadyCallback(symbols); } catch (error) { onResultReadyCallback([]); } } ``` **Parameters:** - `userInput` (string): Search query - `exchange` (string): Exchange filter (can be an empty string) - `symbolType` (string): Symbol type filter (can be an empty string) - `onResultReadyCallback` (function): Callback with search results ### `searchSymbolsPaginated(options, onResult)` _(Optional)_ Paginated symbol search. When implemented, preferred over `searchSymbols` for the chart search box (first page: `offset: 0`, `limit: 50`). Lab: [`phase6-searchSymbolsPaginated-lab.html`](../../examples/phase6-searchSymbolsPaginated-lab.html). ```javascript searchSymbolsPaginated(options, onResult) { const { userInput, exchange, symbolType, limit, offset } = options; const page = yourBackend.searchPage({ userInput, exchange, symbolType, limit, offset }); onResult(page); // SearchResult[] or { searchInProgress, items } } ``` **Parameters:** - `options` — `{ userInput, exchange, symbolType, limit, offset }` - `onResult` — callback with a page of results ### `subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback)` Subscribes to real-time tick data for the specified symbol. **Parameters:** - `symbolInfo` - Symbol information object - `resolution` - Resolution object with `{scale, units}` format - `onRealtimeCallback` - Function to call with real-time trade data - `subscriberUID` - Unique identifier for this subscription - `onResetCacheNeededCallback` - Function to call when cache reset is needed **Implementation Example:** ```javascript subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) { // Subscribe to real-time data stream const channel = `publicTrade.${symbolInfo.symbol}`; this.websocket.subscribe(channel, (data) => { // Process each trade in the data array data.forEach(trade => { const { T: timestamp, s, S: side, p: price, i, v: size } = trade; const tradeMessage = { type: "trade", productId: `${symbolInfo.exchange}:${symbolInfo.segment}:${s}`, symbol: s, exchange: symbolInfo.exchange, segment: symbolInfo.segment, timeStamp: new Date(timestamp), tradeID: i, price: Number(price), quantity: Number(size), amount: Number(price) * Number(size), side: side.toUpperCase(), }; onRealtimeCallback(tradeMessage); }); }); } ``` ### `unsubscribeTicks(subscriberUID)` Unsubscribes from real-time tick data. ```javascript unsubscribeTicks(subscriberUID) { // Remove the subscription this.websocket.unsubscribe(subscriberUID); } ``` ### `subscribeBars(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback)` _(Optional)_ Push complete OHLCV bars for the chart resolution. The SDK applies them directly (no tick aggregation). Mutually exclusive with `subscribeTicks` — if both exist, ticks win. Lab: [`phase6-subscribeBars-lab.html`](../../examples/phase6-subscribeBars-lab.html). ```javascript subscribeBars(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) { // onRealtimeCallback({ time /* ms */, open, high, low, close, volume }) } ``` ### `unsubscribeBars(subscriberUID)` _(Optional)_ ```javascript unsubscribeBars(subscriberUID) { // Cancel the bar subscription } ``` ### UDF adapter — `UDFCompatibleDatafeed` Ready-made Datafeed that speaks the TradingView UDF HTTP protocol: ```javascript import { createChart, UDFCompatibleDatafeed } from "@gocharting/chart-sdk"; const datafeed = new UDFCompatibleDatafeed("https://demo-feed-data.tradingview.com"); createChart("#chart", { symbol: "AAPL", interval: "1D", datafeed }); ``` Endpoints used: `/config` → `onReady`, `/symbols` → `resolveSymbol`, `/history` → `getBars`, `/search` → search, optional `/marks`, `/timescale_marks`, `/time`. CodePen-style replica (TV demo UDF + GoCharting chart): [`examples/tv-udf-codepen-replica.html`](../../examples/tv-udf-codepen-replica.html). **Limitation — higher timeframes:** Unlike TradingView Charting Library, GoCharting does **not** build `2D` / `3D` / `6M` / similar from daily bars via `daily_multipliers`. `UDFCompatibleDatafeed` requests the chart interval and plots the response as-is. If the UDF server returns daily-spaced bars for `6M`, the chart will look like daily. Return true bars for the requested resolution (or pre-aggregate in the host). See [TV → GC migration — Known limitations](../resources/tv-to-gocharting-migration.md#known-limitations). Inject a custom `request` for labs/tests: ```javascript new UDFCompatibleDatafeed("https://mock", { request: (pathWithQuery) => myMock.handle(pathWithQuery), }); ``` ### `destroy()` _(Optional)_ Cleanup method called when the chart is destroyed. Use it to clean up resources such as active WebSocket connections, pending HTTP requests (AbortController), intervals/timeouts, and cached data. ```javascript destroy() { this.websocket?.close(); this.subscriptions.clear(); } ``` ## Bar-to-Tick Conversion Helper Prefer native `subscribeBars` when your feed already has OHLCV. The helper below is for hosts that only implement `subscribeTicks` but receive aggregated bars. ### When to Use This - Your WebSocket or API returns aggregated bar data (OHLC) instead of raw trades - You want to update the chart in real-time but only have bar-level granularity - Your data provider streams 1-second or 1-minute bars ### Helper Function ```javascript /** * Converts a real-time bar (OHLC) into individual ticks. * * Tick order depends on bar direction: * - Bullish bar (Close > Open): Open → Low → High → Close * - Bearish bar (Close < Open): Open → High → Low → Close * * @param {Object} bar - Bar with { timestamp, open, high, low, close, volume } * @param {Object} symbolInfo - Symbol information from resolveSymbol * @returns {Array} Array of 4 tick objects ready for onRealtimeCallback */ function convertBarToTicks(bar, symbolInfo) { const { timestamp, open, high, low, close } = bar; const barTimestamp = typeof timestamp === "number" ? timestamp : new Date(timestamp).getTime(); const isBullish = close > open; // Determine tick order based on bar direction // Bullish: O → L → H → C (price dips first, then rises) // Bearish: O → H → L → C (price rises first, then falls) const tickSequence = isBullish ? [ { price: open, label: "OPEN", side: "BUY" }, { price: low, label: "LOW", side: "BUY" }, { price: high, label: "HIGH", side: "SELL" }, { price: close, label: "CLOSE", side: "BUY" }, ] : [ { price: open, label: "OPEN", side: "SELL" }, { price: high, label: "HIGH", side: "BUY" }, { price: low, label: "LOW", side: "SELL" }, { price: close, label: "CLOSE", side: "SELL" }, ]; // Convert to trade format expected by SDK return tickSequence.map((tick, index) => ({ type: "trade", productId: `${symbolInfo.exchange}:${symbolInfo.segment}:${symbolInfo.symbol}`, symbol: symbolInfo.symbol, exchange: symbolInfo.exchange, segment: symbolInfo.segment, timeStamp: new Date(barTimestamp + (index + 1) * 100), // Small offset between ticks tradeID: `bar_${barTimestamp}_${index + 1}`, price: Number(tick.price), quantity: 1, amount: Number(tick.price), side: tick.side, })); } ``` ### Usage Example ```javascript subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) { // Subscribe to bar stream from your data provider this.barWebSocket.subscribe(`bars.${symbolInfo.symbol}`, (bar) => { // bar = { timestamp: 1704067200000, open: 100.5, high: 101.2, low: 100.1, close: 101.0 } // Convert bar to ticks const ticks = convertBarToTicks(bar, symbolInfo); // Push each tick to the SDK // IMPORTANT: Push Open first, then High/Low (based on direction), then Close last ticks.forEach((tick, index) => { // Optional: Add small delay between ticks for smoother animation setTimeout(() => { onRealtimeCallback(tick); }, index * 50); // 50ms between each tick }); }); } ``` ### Key Points 1. **Always push Open first** - The first tick should be the Open price 2. **Push Close last** - The final tick should always be the Close price 3. **High/Low order matters** - For bullish bars, Low comes before High; for bearish bars, High comes before Low 4. **Use current timestamps** - Ensure timestamps are current (not historical) for the SDK to update the chart 5. **Maintain tick sequence** - Don't randomize the order; it affects how the candle builds visually ## Duration-Based Date Range Helper When implementing `getBars`, you may need to calculate explicit start and end dates based on a duration (e.g., "1D", "1M", "1Y") instead of using a fixed row count like `rows=200`. The SDK provides a utility function for this. ### `getDateRangeForDuration(duration)` Calculates start and end timestamps based on a duration string. This is useful when your data provider API requires explicit date ranges instead of row counts. **Import:** ```javascript import { getDateRangeForDuration } from "@gocharting/chart-sdk"; ``` **Parameters:** - `duration` (string): Duration string - `"1D"`, `"5D"`, `"15D"`, `"1M"`, `"3M"`, `"6M"`, `"1Y"`, `"5Y"`, `"All"` **Returns:** ```javascript { start_date: number, // Start timestamp in milliseconds end_date: number, // End timestamp in milliseconds (current time) interval: string // Recommended interval for this duration } ``` **Duration to Interval Mapping:** | Duration | Interval | Time Range | | -------- | -------- | -------------- | | `"1D"` | `"1m"` | 1 day | | `"5D"` | `"15m"` | 5 days | | `"15D"` | `"30m"` | 15 days | | `"1M"` | `"1h"` | 30 days | | `"3M"` | `"2h"` | 90 days | | `"6M"` | `"4h"` | 180 days | | `"1Y"` | `"1D"` | 365 days | | `"5Y"` | `"1W"` | 5 years | | `"All"` | `"1M"` | 20 years (max) | ### Usage Example ```javascript import { getDateRangeForDuration } from "@gocharting/chart-sdk"; // In your datafeed implementation async getBars(symbolInfo, resolution, periodParams) { const { duration } = periodParams; // If duration is provided, calculate date range if (duration) { const { start_date, end_date, interval } = getDateRangeForDuration(duration); // Fetch data using date range instead of row count const response = await fetch( `/api/bars?symbol=${symbolInfo.symbol}&interval=${interval}&start=${start_date}&end=${end_date}` ); const data = await response.json(); return formatBarsResponse(data); } // Fallback to standard row-based fetching const { from, to, rows } = periodParams; // ... standard implementation } ``` ### When to Use This - Your data provider API requires `start_date` and `end_date` parameters - You want to fetch exactly the right amount of data for a duration (e.g., 1440 bars for 1 day of 1-minute data) - You're implementing duration buttons (1D, 5D, 1M, etc.) in your chart UI - You want to avoid over-fetching or under-fetching data ## Data Formats ### UDF Bar Format (getBars return) ```javascript { s: "ok", // Status: "ok" | "no_data" | "error" t: [1609459200, 1609459260], // Unix timestamps in seconds (array) o: [100.50, 101.20], // Open prices (array) h: [102.75, 103.40], // High prices (array) l: [99.25, 100.80], // Low prices (array) c: [101.80, 102.60], // Close prices (array) v: [1500000, 1200000] // Volumes (array) } ``` ### Trade Message Format (subscribeTicks callback) ```javascript { type: "trade", // Always "trade" productId: "BYBIT:FUTURE:BTCUSDT", // Exchange:Segment:Symbol format symbol: "BTCUSDT", // Symbol name exchange: "BYBIT", // Exchange name segment: "FUTURE", // Market segment timeStamp: new Date(), // JavaScript Date object tradeID: "abc123", // Unique trade identifier price: 45123.45, // Trade price as number quantity: 0.123, // Trade quantity as number amount: 5555.67, // Trade amount (price * quantity) side: "BUY" | "SELL" // Trade side (uppercase) } ``` ### Resolution Object Format ```javascript { scale: "minutes" | "hours" | "days" | "weeks" | "months", units: 1 | 5 | 15 | 30 | 60 | 240 // Number of scale units } // Examples: // 1 minute: { scale: "minutes", units: 1 } // 5 minutes: { scale: "minutes", units: 5 } // 1 hour: { scale: "hours", units: 1 } // 4 hours: { scale: "hours", units: 4 } // 1 day: { scale: "days", units: 1 } ``` ### Symbol Info Format (resolveSymbol return) ```javascript { // Basic Symbol Information exchange: 'BYBIT', // Exchange name segment: 'FUTURE', // Market segment (FUTURE, SPOT, OPTION, etc.) symbol: 'BTCUSDT', // Trading symbol name: 'BTC / USDT PERPETUAL FUTURES', // Display name asset_type: 'CRYPTO', // Asset type: 'CRYPTO' | 'EQUITY' | 'FOREX' | 'COMMODITY' source_id: 'BTCUSDT', // Source identifier from exchange // Symbol Pair Information (for crypto/forex) pair: { from: 'BTC', // Base currency to: 'USDT' // Quote currency }, // Trading Information tradeable: true, // Whether symbol is tradeable is_index: false, // Whether symbol is an index is_formula: false, // Whether symbol is a formula/calculated // Data Feed Information delay_seconds: 600, // Data delay in seconds (0 for real-time) data_status: 'delayed_streaming', // 'streaming' | 'endofday' | 'pulsed' | 'delayed_streaming' data_source_location: 'blr1', // Data source location/datacenter // Display Information industry: 'technology', // Industry classification (optional) symbol_logo_urls: [ // Array of logo URLs (optional) 'https://upload.wikimedia.org/wikipedia/commons/a/a4/Flag_of_the_United_States.svg' ], // Price & Volume Precision contract_size: 1, // Contract size (for futures/options) tick_size: 0.1, // Minimum price movement display_tick_size: 1, // Display tick size volume_size_increment: 0.001, // Minimum volume increment max_tick_precision: 1, // Maximum decimal places for price max_volume_precision: 3, // Maximum decimal places for volume quote_currency: 'USDT', // Quote currency // Futures-specific (if applicable) future_type: 'PERP', // 'PERP' for perpetual, or expiry date for dated futures // Exchange Information exchange_info: { name: 'bybit', // Exchange name (lowercase) code: 'BYBIT', // Exchange code (uppercase) country_cd: 'US', // Country code zone: 'UTC', // Timezone has_unique_trade_id: true, // Whether exchange provides unique trade IDs logo_url: 'https://gocharting.com/avatar/Bybit_Logo.svg', // Exchange logo URL holidays: null, // Array of holiday dates (null if 24/7) hours: [ // Trading hours for each day (0=Sunday, 6=Saturday) { open: true }, // Sunday { open: true }, // Monday { open: true }, // Tuesday { open: true }, // Wednesday { open: true }, // Thursday { open: true }, // Friday { open: true } // Saturday ], contains_ambiguous_symbols: false, // Whether exchange has ambiguous symbol names valid_intervals: [ // Supported timeframes '1m', '3m', '5m', '10m', '15m', '30m', '1h', '2h', '4h', '12h', '1D', '1W', '1M' ] }, // Legacy/Compatibility Fields (still supported) ticker: 'BTCUSDT', // Ticker symbol (same as symbol) full_name: 'BYBIT:FUTURE:BTCUSDT', // Full symbol name with exchange:segment:symbol description: 'BTC / USDT PERPETUAL FUTURES', // Human-readable description (same as name) type: 'crypto', // Symbol type (derived from asset_type) session: '24x7', // Trading session timezone: 'UTC', // Timezone (from exchange_info.zone) has_intraday: true, // Supports intraday data has_daily: true, // Supports daily data supported_resolutions: [ // Supported timeframes (from exchange_info.valid_intervals) '1', '3', '5', '10', '15', '30', '60', '120', '240', '720', '1D', '1W', '1M' ], volume_precision: 3 // Volume decimal places (same as max_volume_precision) } ``` ### Search Results Format (searchSymbols callback) ```javascript [ { symbol: "BTCUSDT", // Symbol identifier full_name: "BYBIT:BTCUSDT", // Full symbol name with exchange description: "Bitcoin / Tether", // Human-readable description exchange: "BYBIT", // Exchange name type: "crypto", // Symbol type key: "BYBIT:FUTURE:BTCUSDT", // REQUIRED: Unique key for symbol resolution and compare functionality }, { symbol: "ETHUSDT", full_name: "BYBIT:ETHUSDT", description: "Ethereum / Tether", exchange: "BYBIT", type: "crypto", key: "BYBIT:FUTURE:ETHUSDT", // REQUIRED: Unique key for symbol resolution and compare functionality type: "crypto", }, ]; ``` ### Period Parameters Format (getBars parameter) ```javascript { from: new Date('2024-01-01'), // Start date (JavaScript Date object) to: new Date('2024-01-31'), // End date (JavaScript Date object) firstDataRequest: true, // Boolean: true for initial load, false for historical rows: 300 // Number of bars to fetch } ``` ### Time Marks Format (getMarks via onDataCallback) ```javascript [ { id: "earnings_2024_q1", // Unique identifier (string|number) time: 1704067200, // Unix timestamp in seconds color: "green", // CSS color for circle text: ["Q1 Earnings", "EPS: $2.45", "Revenue: $12.3B"], // Tooltip (string|array) label: "E", // Single character for circle labelFontColor: "white", // Optional: label text color minSize: 30, // Optional: minimum circle size in pixels }, { id: "news_announcement", time: 1704153600, color: "#ff6b35", text: "Major acquisition announced", label: "N", labelFontColor: "white", minSize: 25, }, ]; ``` ### Search Result Format ```javascript { symbol: 'AAPL', full_name: 'NASDAQ:AAPL', description: 'Apple Inc.', exchange: 'NASDAQ', ticker: 'AAPL', type: 'stock' } ``` ## Trading Data Formats When using the SDK with trading features enabled, you'll need to provide broker data using the `setBrokerAccounts()` method. This section documents the exact format expected for orderBook, tradeBook, and positions. ### Order Book Format (Active Orders) The `orderBook` array contains all active (pending) orders. Each order must follow this structure: ```javascript orderBook: [ { // Required Fields orderId: "ORDER_001", // Unique order identifier (string) datetime: new Date(), // Order creation date (Date object) timeStamp: new Date().getTime(), // Order timestamp in milliseconds (number) status: "open", // Order status: "open" | "filled" | "cancelled" | "rejected" price: 50000, // Order price (number, 0 for market orders) size: 0.1, // Order quantity (number) productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) remainingSize: 0.1, // Remaining unfilled quantity (number) orderType: "limit", // Order type: "limit" | "market" | "stop" | "stopLimit" side: "buy", // Order side: "buy" | "sell" exchange: "BYBIT", // Exchange name (string) symbol: "BTCUSDT", // Base symbol name (string) broker: "demo", // Broker identifier (string) productType: "FUTURE", // Product type: "FUTURE" | "SPOT" | "OPTION" security: { // Security information object symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, key: "demo-BYBIT:FUTURE:BTCUSDT-ORDER_001", // Unique key (broker-productId-orderId) isGC: true, // GoCharting flag (boolean) // Optional Fields lastTradeTimestamp: null, // Last trade timestamp (number | null) cost: null, // Order cost (number | null) trades: [], // Associated trades array fee: { // Fee information currency: "USDT", cost: 0.0, rate: 0.0, }, info: {}, // Additional broker-specific info fillPrice: null, // Fill price if partially filled (number | null) avgFillPrice: null, // Average fill price (number | null) filledSize: 0, // Filled quantity (number) modifiedAt: null, // Last modification time (number | null) takeProfit: null, // Take profit price (number | null) stopLoss: null, // Stop loss price (number | null) paperTraderKey: null, // Paper trading key (string | null) validity: "DAY", // Order validity: "DAY" | "GTC" | "IOC" | "FOK" commissions: 0, // Commission amount (number) stopPrice: null, // Stop trigger price for stop orders (number | null) rejReason: null, // Rejection reason if rejected (string | null) userTag: null, // User-defined tag (string | null) }, ]; ``` ### Trade Book Format (Executed Trades) The `tradeBook` array contains the history of executed trades: ```javascript tradeBook: [ { // Required Fields tradeId: "TRADE_001", // Unique trade identifier (string) orderId: "ORDER_FILLED", // Associated order ID (string) datetime: new Date(Date.now() - 3600000), // Trade execution date (Date object) timeStamp: new Date(Date.now() - 3600000).getTime(), // Trade timestamp in milliseconds (number) status: "filled", // Trade status: "filled" | "partial" price: 49500, // Execution price (number) tradeSize: 0.05, // Trade quantity (number) tradeValue: 2475, // Trade value (price × quantity) (number) productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) orderType: "Market", // Original order type (string) side: "buy", // Trade side: "buy" | "sell" broker: "demo", // Broker identifier (string) productType: "FUTURE", // Product type: "FUTURE" | "SPOT" | "OPTION" security: { // Security information object symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, key: "demo-BYBIT:FUTURE:BTCUSDT-TRADE_001", // Unique key (broker-productId-tradeId) isGC: true, // GoCharting flag (boolean) // Optional Fields lastTradeTimestamp: null, // Last trade timestamp (number | null) cost: null, // Trade cost (number | null) takerOrMaker: null, // Taker/Maker flag: "taker" | "maker" | null fee: { // Fee information currency: "USDT", cost: 2.475, rate: 0.001, }, info: {}, // Additional broker-specific info paperTraderKey: null, // Paper trading key (string | null) type: "tradeExecuted", // Trade type (string) remainingSize: "0", // Remaining order size (string) orderDetails: { // Original order details orderId: "ORDER_FILLED", orderType: "market", }, triggerLogicPending: false, // Trigger logic flag (boolean) spread: 0, // Bid-ask spread at execution (number) }, ]; ``` ### Positions Format (Open Positions) The `positions` array contains current open positions: ```javascript positions: [ { // Required Fields size: 0.05, // Net position size (number, positive = long, negative = short) size_currency: 0.05, // Position quantity in base currency (number) price: 49500, // Average entry price (number) amount: 2475, // Position value (price × quantity) (number) productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) broker: "demo", // Broker identifier (string) productType: "FUTURE", // Product type: "FUTURE" | "SPOT" | "OPTION" currency: "USDT", // Position currency (string) id: "POS_001", // Position identifier (string) underlying: "BYBIT:FUTURE:BTCUSDT", // Underlying asset identifier (string) unPnl: 25, // Unrealized profit/loss (number) rPnl: 0, // Realized profit/loss (number) pnl: 25, // Total profit/loss (number) security: { // Security information object symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, symbol: "BTCUSDT", // Base symbol name (string) segment: "FUTURE", // Market segment (string) exchange: "BYBIT", // Exchange name (string) key: "demo-BYBIT:FUTURE:BTCUSDT-POS_001", // Unique key (broker-productId-positionId) isGC: true, // GoCharting flag (boolean) // Optional Fields openPosVal: 0, // Open position value (number) closedPosVal: 0, // Closed position value (number) paperTraderKey: null, // Paper trading key (string | null) pnlMultiplier: 1, // PnL calculation multiplier (number) positions: { // Detailed position breakdown (object) "demo-BYBIT:FUTURE:BTCUSDT-FUTURE": { positionId: "demo-BYBIT:FUTURE:BTCUSDT-FUTURE", positionFlag: true, security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, positionStatus: "open", // Position status: "open" | "closed" entry: 49500, // Entry price (number) exit: null, // Exit price if closed (number | null) size: 0.05, // Position size (number) positionSize: 0.05, // Position size duplicate (number) transaction_type: "real", // Transaction type: "real" | "paper" trades: [], // Associated trades array broker: "demo", currency: "USDT", productId: "BYBIT:FUTURE:BTCUSDT", ticker: "BYBIT:FUTURE:BTCUSDT", created: null, // Creation timestamp (number | null) symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", underlying: "BYBIT:FUTURE:BTCUSDT", series: null, // Series for options (string | null) strike: null, // Strike price for options (number | null) optionType: null, // Option type: "call" | "put" | null expiration_timestamp: null, // Expiration timestamp for options (number | null) strategyId: "1000", strategyType: "1000", strategyLabel: "Real", strategyColor: "#000000", strategyFlag: true, strategyStatus: "open", stratgeyFormulae: null, fee: { currency: "USDT", cost: 0.0, rate: 0.0, }, side: "buy", // Position side: "buy" | "sell" modified: new Date().getTime(), // Last modified timestamp (number) productType: "FUTURE", rpnl: 0, // Realized PnL (number) totalBuyQty: 0.05, // Total buy quantity (number) avgBuyPrice: 49500, // Average buy price (number) totalSellQty: 0, // Total sell quantity (number) avgSellPrice: 0, // Average sell price (number) }, }, }, ]; ``` ### Account List Format The `accountList` array contains trading account information: ```javascript accountList: [ { // Required Fields account_id: "DEMO_001", // Unique account identifier (string) AccountID: "DEMO_001", // Alternative account ID field (string) currency: "USD", // Account base currency (string) balance: 100000, // Current account balance (number) // Optional Fields AccountType: "Demo Trading", // Account type description (string) label: "Demo Account (Trading)", // Display label for UI (string) equity: 100000, // Account equity value (number) margin: 0, // Used margin amount (number) freeMargin: 100000, // Available margin for trading (number) }, ]; ``` ### Complete setBrokerAccounts Example ```javascript // Complete example with all trading data chartInstance.setBrokerAccounts({ accountList: [ { account_id: "DEMO_001", AccountID: "DEMO_001", AccountType: "Demo Trading", label: "Demo Account", currency: "USD", balance: 100000, equity: 100000, margin: 0, freeMargin: 100000, }, ], orderBook: [ { orderId: "ORDER_001", datetime: new Date(), timeStamp: new Date().getTime(), status: "open", price: 50000, size: 0.1, productId: "BYBIT:FUTURE:BTCUSDT", remainingSize: 0.1, orderType: "limit", side: "buy", exchange: "BYBIT", symbol: "BTCUSDT", broker: "demo", productType: "FUTURE", security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, key: "demo-BYBIT:FUTURE:BTCUSDT-ORDER_001", isGC: true, validity: "GTC", fee: { currency: "USDT", cost: 0.0, rate: 0.001 }, }, ], tradeBook: [ { tradeId: "TRADE_001", orderId: "ORDER_FILLED", datetime: new Date(Date.now() - 3600000), timeStamp: new Date(Date.now() - 3600000).getTime(), status: "filled", price: 49500, tradeSize: 0.05, tradeValue: 2475, productId: "BYBIT:FUTURE:BTCUSDT", orderType: "market", side: "buy", broker: "demo", productType: "FUTURE", security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, key: "demo-BYBIT:FUTURE:BTCUSDT-TRADE_001", isGC: true, fee: { currency: "USDT", cost: 2.475, rate: 0.001 }, }, ], positions: [ { size: 0.05, size_currency: 0.05, price: 49500, amount: 2475, productId: "BYBIT:FUTURE:BTCUSDT", broker: "demo", productType: "FUTURE", currency: "USDT", id: "POS_001", underlying: "BYBIT:FUTURE:BTCUSDT", unPnl: 25, rPnl: 0, pnl: 25, security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, symbol: "BTCUSDT", segment: "FUTURE", exchange: "BYBIT", key: "demo-BYBIT:FUTURE:BTCUSDT-POS_001", isGC: true, }, ], }); ``` **Important Notes:** 1. **productId Format**: Always use `EXCHANGE:SEGMENT:SYMBOL` format (e.g., `"BYBIT:FUTURE:BTCUSDT"`) 2. **Timestamps**: Use JavaScript `Date` objects for `datetime` fields and milliseconds for `timeStamp` fields 3. **Unique Keys**: The `key` field should follow the pattern `broker-productId-id` for uniqueness 4. **Security Object**: Always include the security object with at least `symbol`, `exchange`, `segment`, `tick_size`, and `lot_size` 5. **Side Values**: Use lowercase `"buy"` or `"sell"` for consistency 6. **Status Values**: Use lowercase status strings (`"open"`, `"filled"`, `"cancelled"`, etc.) For more details on trading integration, see the [Trading API Documentation](./trading.md). ## Complete Example Here's a complete example implementing a datafeed for a REST API: ```javascript // Create a complete datafeed object for REST API integration const apiDatafeed = { apiBaseUrl: 'https://api.example.com', apiKey: 'your-api-key', subscriptions: new Map(), async getBars(symbolInfo, resolution, periodParams) { const { from, to } = periodParams; try { const response = await fetch( `${this.apiBaseUrl}/bars?symbol=${symbolInfo.name}&resolution=${resolution}&from=${from}&to=${to}`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, } ); const data = await response.json(); return { bars: data.bars.map((bar) => ({ time: bar.time * 1000, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })), meta: { noData: data.bars.length === 0, }, }; } catch (error) { console.error("Failed to fetch bars:", error); return { bars: [], meta: { noData: true } }; } } async resolveSymbol(symbolName, onResolve, onError) { try { const response = await fetch( `${this.apiBaseUrl}/symbols/${symbolName}`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, } ); const symbolData = await response.json(); const symbolInfo = { ...symbolData, // full GoCharting security from API ticker: symbolData.ticker || symbolData.symbol, full_name: symbolData.full_name || `${symbolData.exchange}:${symbolData.segment}:${symbolData.symbol}`, description: symbolData.description || symbolData.name, type: symbolData.type || (symbolData.asset_type || "EQUITY").toLowerCase(), }; onResolve(symbolInfo); } catch (error) { onError("Symbol not found"); } } async searchSymbols(userInput, exchange, symbolType, onResultReadyCallback) { try { const response = await fetch( `${this.apiBaseUrl}/search?q=${encodeURIComponent(userInput)}`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, } ); const results = await response.json(); onResultReadyCallback(results); } catch (error) { onResultReadyCallback([]); } } }; // Usage - pass the datafeed object directly with simplified API const chart = createChart("#chart", { symbol: "NASDAQ:AAPL", interval: "1D", datafeed: apiDatafeed, // Pass the object directly licenseKey: "YOUR_LICENSE_KEY", }); ``` ## Related Documentation - **[ProfessionalChart API](./chart.md)** - Main chart component - **[Configuration API](./configuration.md)** - Chart configuration options - **[Examples](../examples/)** - Working implementations - **[Integration Tutorial](../tutorials/basic-integration.md)** - Step-by-step guide --- _For more advanced data provider implementations, see the [examples section](../examples/)._ File: docs/api/enums.md ================================== # Enums & literal unions Catalog of **real** GoCharting TypeScript union types and related literal values. This is not a third-party enum mirror — only types that exist in the SDK. Full definitions live on **[TypeScript Types](./types.md#enums--literal-unions)**. Usage: [Chart API](./chart.md), [Trading API](./trading.md), [Events](./events.md). ## Chart / widget | Type | Members / shape | Where used | | ---- | --------------- | ---------- | | [`ChartActionId`](./types.md#chartactionid) | `chartReset`, `zoomIn`, `zoomOut`, `undo`, `redo`, `invertScale`, `logScale`, `magnet` | `activeChart().executeActionById()` | | Chart type strings | Runtime list from `supportedChartTypes()` (e.g. `CANDLESTICK`, `LINE`, …) — not a fixed TS enum yet | `setChartType`, `supportedChartTypes` | | [`VisibilityType`](./types.md#visibilitytype) | `alwaysOn`, `alwaysOff`, `visibleOnMouseOver` | Navigation / pane button visibility APIs | | [`ChartTheme`](./types.md#charttheme) | `light`, `dark` | `theme` / `setTheme` | | [`ChartFeatureId`](./types.md#chartfeatureid) | `topBar`, `bottomBar`, `drawingToolbar`, `leftPanel`, `rightPanel`, `symbolSearch`, `compare` | `features()` / `setFeatureEnabled` | | [`MarketStatusInfo`](./types.md#marketstatusinfo) | `{ isOpen?, statusText?, dataStatus?, delaySeconds? }` | `activeChart().marketStatus()` | | [`TimeFrameInput`](./types.md#timeframeinput) | `string` \| `{ from, to }` \| period-back / time-range objects | `setTimeFrame` | ### `ChartActionId` ```typescript type ChartActionId = | "chartReset" | "zoomIn" | "zoomOut" | "undo" | "redo" | "invertScale" | "logScale" | "magnet"; ``` ```javascript chart.activeChart().executeActionById("zoomIn"); ``` See [Chart API](./chart.md). ### Chart type strings There is no numeric series-type enum. Use string ids returned by the widget: ```javascript console.log(chart.supportedChartTypes()); // e.g. ["CANDLESTICK", "LINE", …] chart.setChartType("LINE"); ``` ### `VisibilityType` ```typescript type VisibilityType = | "alwaysOn" | "alwaysOff" | "visibleOnMouseOver"; ``` ```javascript chart.navigationButtonsVisibility().setValue("alwaysOn"); chart.paneButtonsVisibility().setValue("alwaysOff"); ``` ### `ChartTheme` ```typescript type ChartTheme = "light" | "dark"; ``` ### `ChartFeatureId` ```typescript type ChartFeatureId = | "topBar" | "bottomBar" | "drawingToolbar" | "leftPanel" | "rightPanel" | "symbolSearch" | "compare"; ``` ### `MarketStatusInfo` ```typescript type MarketStatusInfo = { isOpen?: boolean; statusText?: string; dataStatus?: string; delaySeconds?: number; }; ``` ### `TimeFrameInput` ```typescript type TimeFrameInput = | string | { from: number; to: number } | { type: "period-back"; value: string } | { type: "time-range"; from: number; to: number }; ``` ## Trading | Type | Members | Where used | | ---- | ------- | ---------- | | [`OrderType`](./types.md#ordertype) | `market`, `limit`, `stop`, `stop_limit` | Orders / broker payloads | | [`OrderSide`](./types.md#orderside) | `buy`, `sell` | Orders / positions | | [`OrderStatus`](./types.md#orderstatus) | `open`, `pending`, `filled`, `cancelled`, `rejected`, `expired` | Orders | | [`TimeInForce`](./types.md#timeinforce) | `GTC`, `IOC`, `FOK`, `DAY` | Orders | | [`ExecutionDirection`](./types.md#executiondirection) | `buy`, `sell` | Execution shapes | ### `OrderType` / `OrderSide` / `OrderStatus` / `TimeInForce` ```typescript type OrderType = "market" | "limit" | "stop" | "stop_limit"; type OrderSide = "buy" | "sell"; type OrderStatus = | "open" | "pending" | "filled" | "cancelled" | "rejected" | "expired"; type TimeInForce = "GTC" | "IOC" | "FOK" | "DAY"; ``` See [Trading API](./trading.md) and [TypeScript Types — Trading](./types.md#trading-types). ### `ExecutionDirection` ```typescript type ExecutionDirection = "buy" | "sell"; ``` ## Events | Type | Members | Where used | | ---- | ------- | ---------- | | [`TradingEventType`](./types.md#tradingeventtype) | `CREATE_ORDER`, `PLACE_ORDER`, `MODIFY_ORDER`, `CANCEL_ORDER` | Narrow trading subset | | [`AppCallbackEventType`](./types.md#appcallbackeventtype) | Full `appCallback` event name union | `appCallback` | ### `TradingEventType` ```typescript type TradingEventType = | "CREATE_ORDER" | "PLACE_ORDER" | "MODIFY_ORDER" | "CANCEL_ORDER"; ``` ### `AppCallbackEventType` ```typescript type AppCallbackEventType = | "CREATE_ORDER" | "PLACE_ORDER" | "EDIT_ORDER" | "PLACE_ORDER_DIRECT" | "MODIFY_POSITION" | "MODIFY_ORDER_DIRECT" | "MODIFY_ORDER" | "CANCEL_ORDER" | "CLOSE_POSITION" | "EXIT_ALL_POSITIONS" | "CANCEL_ALL_ORDERS" | "DOWNLOAD_MORE_DATA_BY_DATE" | "DOWNLOAD_MORE_DATA" | "OPEN_SETTINGS" | "OPEN_TRADING_WIDGET" | "OPEN_CONTEXT_MENU" | "CHART_SELECTED" | "CHART_MODE_CHANGED"; ``` See [Events API](./events.md). ## Out of scope This catalog does **not** invent enums for APIs the SDK does not expose (for example broker bottom-bar modes, drawing share modes, or a full UI action id catalog). When new unions ship in `index.d.ts`, they will be added here. ## Related - [TypeScript Types](./types.md) - [Chart API](./chart.md) - [Trading API](./trading.md) - [Events](./events.md) File: docs/api/events.md =================================== # Events API ## Event System Overview The GoCharting SDK reports events through **three channels**: | Channel | How you listen | Typical use | | ------------- | ---------------------------------------------------------------- | -------------------------------- | | Config callbacks | `onReady`, `onError`, `appCallback` in `createChart()` / `` | Lifecycle + trading / UI actions | | Widget bus | `chart.subscribe(event, cb)` / `chart.unsubscribe(event, cb)` | Layout, active chart, autosave, screenshots | | Chart instance | `chart.activeChart().onSymbolChanged().subscribe(…)` etc. | Per-pane symbol, interval, range, crosshair | ```javascript const chart = window.GoChartingSDK.createChart("#chart-container", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: myDatafeed, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", onReady: (chartInstance) => { console.log("Chart is ready!", chartInstance); }, onError: (error) => { console.error("Chart error:", error); }, appCallback: (event) => { console.log(event.eventType, event.message); }, }); chart.subscribe("layout", (info) => { console.log("Layout changed:", info.layout, info.charts?.length); }); chart.subscribe("activeChartChanged", (msg) => { console.log("Active chart:", msg.idx, msg.chartId); }); ``` ## The `AppCallbackEvent` Object `appCallback` receives **a single event object** (not separate arguments): ```typescript interface AppCallbackEvent { eventType: string; // Event name, e.g. "PLACE_ORDER" message: any; // Event payload (shape depends on eventType) onClose: ((...args: any[]) => void) | undefined; // Closes the SDK popup/UI that raised the event (when applicable) } ``` - `eventType` — the event name (see the tables below). - `message` — the event payload; its shape depends on the event type. - `onClose` — some events are raised from an SDK popup (e.g. the order form). Call `onClose()` after handling the event to dismiss that popup. It is `undefined` for events that have no UI to close. ```javascript appCallback: (event) => { switch (event.eventType) { case "PLACE_ORDER": submitToBroker(event.message.order); if (event.onClose) event.onClose(); // dismiss the order form break; case "CREATE_ALERT": console.log("Alert at price:", event.message.alert.price); break; case "CHART_MODE_CHANGED": console.log("Layout:", event.message.layout); break; case "CHART_SELECTED": console.log("Active chart:", event.message.chartId); break; } }; ``` > **TypeScript:** With the shipped type definitions, `event.message` is automatically narrowed based on `event.eventType` inside `switch` statements (see [Type Definitions](./types.md)). --- ## Widget `subscribe` / `unsubscribe` Prefer the widget bus when you want TV-style listeners on the chart instance (no config required). Mapped names are aliases of `appCallback` chart events; raw `eventType` strings are also accepted. ```javascript function onLayout(info) { console.log(info.layout, info.isMultichartingEnabled, info.charts); } chart.subscribe("layout", onLayout); chart.unsubscribe("layout", onLayout); // Equivalent raw name: chart.subscribe("CHART_MODE_CHANGED", onLayout); ``` | Subscribe event | From `appCallback` | Payload | When it fires | | ----------------------- | ---------------------- | ----------------- | --------------------------------------------------------------- | | `layout` | `CHART_MODE_CHANGED` | `MultiChartInfo` | Multichart grid changes (`setLayout`, layout chooser, etc.) | | `activeChartChanged` | `CHART_SELECTED` | Chart selected msg | Active pane changes (`setActiveChart`, click, sync) | | `onAutoSaveNeeded` | — | `undefined` | Debounced dirty signal (drawings/indicators); call host save | | `onChartLoaded` | — | `{ blob }` | After a successful `load()` / restore | | `chart_load_requested` | — | `{ blob }` | Host asked to load; before apply completes | | `onScreenshotReady` | — | image URL `string` | After `takeScreenshot()` resolves with a saved URL | | `drawing_event` | — | `DrawingEventParams` | Drawing created or removed (`type`, `objectId`, `chartId`) | > **Layout changes always notify.** `setLayout("2")` (and UI layout switches) emit `CHART_MODE_CHANGED` to `appCallback` **and** `layout` on `chart.subscribe`. Labs: `phase2-layout-lab.html`, `phase0-1a-api-lab.html`, `phase5-persistence-lab.html`. ### `MultiChartInfo` (`layout` / `CHART_MODE_CHANGED`) ```javascript { isMultichartingEnabled: true, layout: "1|1", // GoCharting layout id charts: [ { id, chartId, idx: 0, symbol: "BTCUSDT", interval: "1m" }, { id, chartId, idx: 1, symbol: "ETHUSDT", interval: "5m" }, ], } ``` ### Persistence (Phase 5) ```javascript chart.subscribe("onAutoSaveNeeded", () => { chart.saveChartToServer?.(); }); chart.subscribe("onChartLoaded", ({ blob }) => { console.log("Restored layout", blob?.layout); }); ``` ### Screenshots ```javascript chart.subscribe("onScreenshotReady", (imageUrl) => console.log(imageUrl)); await chart.takeScreenshot(); ``` --- ## Chart-level subscriptions (`activeChart()`) Per-pane observers return an `ISubscription` (`subscribe(cb)` → unregister). See [Chart API](./chart.md): | Method | Payload | Fires when | | ------------------------- | -------------------------- | -------------------------------- | | `onSymbolChanged()` | `void` | Security / symbol changes | | `onIntervalChanged()` | `void` | Interval changes | | `onChartTypeChanged()` | `void` | Chart type changes | | `onDataLoaded()` | `void` | Loading finishes | | `onVisibleRangeChanged()` | `{ from, to }` (unix sec) | Visible time range changes | | `crossHairMoved()` | `{ time, price }` | Crosshair moves | | `onHoveredSourceChanged()` | entity info or `null` | Hovered study/drawing changes | | `onDrawingEvent()` | `DrawingEventParams` | Drawing created or removed | ```javascript const api = chart.activeChart(); const unsub = api.onSymbolChanged().subscribe(() => { console.log("Symbol:", api.symbol()); }); api.onDrawingEvent().subscribe(({ type, objectId }) => { console.log(type, objectId); }); ``` Lab: `phase1e-api-lab.html`. --- ## `appCallback` Event Types ### Trading Events Emitted when trading is enabled (`trading.enableTrading: true`) and the user interacts with trading UI (order forms, order/position lines, context menus). | Event Type | Message Payload | Description | | -------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------ | | `PLACE_ORDER` | `{ order, security, ltp }` | User submitted an order (order form, one-click trade, context menu) | | `MODIFY_ORDER` | `{ orderId, order, security }` | User modified an existing order (e.g. dragged the order line/TP/SL) | | `EDIT_ORDER` | `{ order }` | User opened/edited an order via the order editing UI | | `CANCEL_ORDER` | `{ order }` | User requested cancellation of an order | | `MODIFY_POSITION` | `{ position }` (includes `update`/`updateType` fields) | User modified a position (e.g. dragged position TP/SL) | | `CLOSE_POSITION` | `{ position, chartIdx }` | User clicked the position **X** (close) button | | `EXIT_ALL_POSITIONS` | Position data | User requested to close all positions | | `CANCEL_ALL_ORDERS` | Order data | User requested to cancel all orders | **`PLACE_ORDER` message example:** ```javascript { order: { side: "buy", // "buy" | "sell" orderType: "market", // "market" | "limit" | "stop" | "stopLimit" price: 50000, // 0/undefined for market orders size: 0.1, task: "placement", stopLoss: 49000, // optional takeProfit: 52000, // optional }, security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", /* ... */ }, ltp: 50100, // last traded price } ``` ### Alert Events | Event Type | Message Payload | Description | | -------------- | ---------------------------------------- | -------------------------------------------------- | | `CREATE_ALERT` | `{ alert: { symbol, price, condition, message? } }` | User created an alert (context menu or bottom bar) | | `MODIFY_ALERT` | Alert data | User modified an existing alert | ### Chart Events | Event Type | Message Payload | Description | | -------------------- | -------------------------------------------- | ------------------------------------------------------- | | `CHART_SELECTED` | `{ id, chartId, idx, symbol, interval }` | Active chart changed — also `subscribe("activeChartChanged")` | | `CHART_MODE_CHANGED` | `MultiChartInfo` (see above) | Layout changed — also `subscribe("layout")` | > **Note:** `ON_LOAD` is handled **internally** by the SDK and is **not** delivered to your `appCallback`. Use `onReady` instead. ### Save / Template Events Emitted when the user saves charts or manages indicator templates from the chart UI. Widget autosave uses `onAutoSaveNeeded` / `onChartLoaded` on `chart.subscribe` instead. | Event Type | Message Payload | Description | | -------------------------- | ---------------------------------------------------------------------- | ---------------------------------------- | | `SAVE_TEMPLATE` | `{ key, name, data_type: "study", action: "INSERT", value, template }` | User saved an indicator template | | `DELETE_TEMPLATE` | `{ key, data_type: "study", action: "DELETE", template }` | User deleted an indicator template | | `SAVE_CHART` | `{ data_type: "chart", action: "INSERT", key, name, value }` | User saved the chart layout | > **Note:** `SAVE_CONFIGURATION_STORE` is handled **internally** and is **not** forwarded to your `appCallback`. ### Data Events | Event Type | Message Payload | Description | | ---------------------------- | -------------------- | -------------------------------------------------------------------------- | | `DOWNLOAD_MORE_DATA_BY_DATE` | `{ params, store }` | User jumped to a date (`params` is `[start_date, end_date]`); the SDK also requests the data itself | > **Note:** Some events (e.g. `DOWNLOAD_MORE_DATA`, `OPEN_TRADING_WIDGET`, `DELETE_ALERT`, `PLACE_ORDER_DIRECT`, `MODIFY_ORDER_DIRECT`) are handled internally by the SDK and are not reliably forwarded to your `appCallback`. Unrecognized event types are forwarded as-is, so always include a `default` branch that ignores events you do not handle. ## Complete Example ```javascript const chart = window.GoChartingSDK.createChart("#chart-container", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: myDatafeed, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", trading: { enableTrading: true, showOpenOrders: true, showPositions: true, }, appCallback: (event) => { switch (event.eventType) { case "PLACE_ORDER": console.log("Order:", event.message.order); if (event.onClose) event.onClose(); break; case "MODIFY_ORDER": console.log("Modify order:", event.message.orderId); break; case "CANCEL_ORDER": console.log("Cancel order:", event.message.order); break; case "MODIFY_POSITION": console.log("Modify position:", event.message.position); break; case "CLOSE_POSITION": console.log("Close position:", event.message.position); break; case "CREATE_ALERT": console.log("Alert:", event.message.alert.price); break; case "SAVE_TEMPLATE": saveToBackend(event.message.template); break; case "CHART_MODE_CHANGED": console.log("Layout:", event.message.layout); break; case "CHART_SELECTED": console.log("Chart selected:", event.message.chartId); break; default: break; } }, onReady: (c) => { c.subscribe("layout", (info) => console.log("layout", info.layout)); c.subscribe("onAutoSaveNeeded", () => c.saveChartToServer?.()); }, }); ``` ## Related Documentation - **[ProfessionalChart API](./chart.md)** - Main chart component - **[GoCharting React Component](./gocharting-component.md)** - Declarative React API - **[Configuration API](./configuration.md)** - Chart configuration - **[Trading API](./trading.md)** - Trading integration details - **[Type Definitions](./types.md)** - Typed event messages - **[Trading Integration](../tutorials/trading-integration.md)** - Trading features - **[Examples](../examples/)** - Working implementations --- _For more event handling examples, see the [tutorials section](../tutorials/)._ File: docs/api/gocharting-component.md ================================================= # GoCharting React Component `` is the **declarative React API** for the GoCharting SDK — an alternative to imperative [`createChart()`](./chart.md). Mount the component; React owns mount/unmount cleanup. Props mirror [`ChartConfig`](./types.md#chartconfig) (same surface as `createChart`). Details for each option: **[Configuration](./configuration.md)**. ## Quick start ```jsx import { GoCharting } from "@gocharting/chart-sdk"; function App() { return ( { chart.addIndicator({ type: "EMA", id: "ema-20" }); }} appCallback={(event) => { console.log(event.eventType, event.message); }} /> ); } ``` Sample app: **[React and TypeScript](/framework-integration/react-typescript)** ([GitHub](https://github.com/GoChartingInc/gocharting-sdk-examples/tree/main/react-typescript)). ### UMD (no JSX) ```javascript const { React, ReactDOM, GoCharting } = window.GoChartingSDK; const root = ReactDOM.createRoot(document.getElementById("chart")); root.render( React.createElement(GoCharting, { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: myDatafeed, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", }), ); ``` > [!NOTE] > > Older UMD demos may use `ReactDOM.render`. Prefer `createRoot` on React 18+. --- ## Props Full field list and defaults: **[Configuration](./configuration.md)** / **[`ChartConfig`](./types.md#chartconfig)**. Below is the React-oriented summary. ### Core | Prop | Type | Description | | ------------ | ---------- | ---------------------------------------- | | `symbol` | `string` | Initial symbol | | `interval` | `string` | Initial interval | | `datafeed` | [`Datafeed`](./datafeed.md) | Bars / resolve / optional ticks & marks | | `licenseKey` | `string` | SDK license key (required) | ### Display | Prop | Type | Default | Description | | ------------ | ------------------ | --------- | ------------------------------------ | | `theme` | `"light" \| "dark"` | `"light"` | Named theme | | `themeColor` | `string` | — | Hex background (see [Themes](./themes.md)) | | `locale` | `string` | `"en-US"` | UI locale | | `autosize` | `boolean` | `true` | AutoFit | | `width` / `height` | `string \| number` | `"100%"` | Box size | ### Callbacks | Prop | Type | Description | | ------------- | ---------- | ------------------------------------------------------------------------------- | | `appCallback` | `(event) => void` | Single `{ eventType, message, onClose }` — [Events](./events.md) | | `onReady` | `(instance) => void` | Imperative helpers when mounted (see [Ref](#imperative-access-via-ref)) | | `onError` | `(error) => void` | Construct / runtime errors | ### Trading & chrome | Prop | Type | Description | | --------------------------- | --------- | ------------------------------------------------------ | | `trading` | [`TradingConfig`](./types.md#tradingconfig) | `enableTrading`, panels, stop orders, … | | `exclude` | [`ExcludeOptions`](./types.md#excludeoptions) | Hide panels / indicators | | `disableSearch` / `disableCompare` / `hideDrawingToolBar` | `boolean` | Top-bar / drawing chrome | | `contextMenu` | [`ContextMenuOptions`](./types.md#contextmenuoptions) | Alias: `context_menu` | | `popups` | [`PopupsConfig`](./types.md#popupsconfig) | Panel state overrides | | `favourite` / `favorites` | [`FavouriteConfig`](./types.md#favouriteconfig) | Favorites (`favorites` = TV-shaped map) | | `defaultInitialChartConfig` | [`DefaultInitialChartConfig`](./types.md#defaultinitialchartconfig) | Settings / appearance seed | | `autoSave` | `boolean` | `sessionStorage` persistence (default `true`) | | `showCrosshairPlusIcon` | `boolean` | Crosshair plus (default `true`) | | `isNativeApp` / `touchMode` | `boolean` | WebView / JS mobile shell — [Mobile WebView](../guides/mobile-integration.md) | | `debugLog` | `boolean` | Console diagnostics | ### Construct-time (Phase 4) Same as `createChart` — applied at mount: | Prop | Description | | --------------------- | ----------- | | `disabled_features` / `enabled_features` | Featureset → chrome flags | | `overrides` / `studies_overrides` / `settings_overrides` | Appearance / studies / settings | | `timezone` | → `settings.zone` | | `studies_access` | Soft indicator blacklist → `exclude.indicators` | Persistence props (`auto_save_delay`, `save_load_adapter`, `saved_data`, `snapshot_url`, …) are accepted via the open props bag when you pass them through — see [Configuration → Persistence](./configuration.md#persistence). > [!NOTE] > > Unlike `createChart()`, the component does **not** expose `skipLicenseValidation`. A valid `licenseKey` is always required (use the demo key for evaluation). --- ## Imperative access via ref `ref` and `onReady` receive the **ProfessionalChart** helper surface (`setSymbol`, `setInterval`, `addIndicator`, `setBrokerAccounts`, `resubscribeAll`, `*AtIndex`, …) — same family as **[Chart Widget](./chart-widget.md)**. They do **not** expose the Phase 0–5 widget façade (`activeChart()`, `setLayout()`, `subscribe()`, `applyOverrides()`, …). For that, use [`createChart()`](./chart.md) and keep the returned instance. ```jsx import React, { useRef } from "react"; import { GoCharting } from "@gocharting/chart-sdk"; function ChartApp() { const chartRef = useRef(null); return (
); } ``` Or from `onReady`: ```jsx { chart.addIndicator({ type: "RSI", id: "rsi-14" }); }} /> ``` --- ## Trading example ```jsx { switch (event.eventType) { case "PLACE_ORDER": submitToBroker(event.message.order); if (event.onClose) event.onClose(); break; case "CANCEL_ORDER": cancelAtBroker(event.message.order); break; } }} onReady={(chart) => { chart.setBrokerAccounts(myBrokerData); }} /> ``` More: [Trading](./trading.md), [Events](./events.md). --- ## `` vs `createChart()` | Aspect | `` | `createChart()` | | ------------------- | -------------------------------------------- | ---------------------------------------- | | Style | Declarative (JSX) | Imperative | | Lifecycle | React unmount cleans up | Call `chart.destroy()` | | Config | Props ≈ [`ChartConfig`](./types.md#chartconfig) | Config object | | Convenience helpers | `ref` / `onReady` (Chart Widget–style) | Same helpers on the return value | | Phase 0–5 façade | Not on ref — use `createChart` | `activeChart()`, `setLayout()`, `subscribe()`, … | | Best for | React apps | Vanilla JS / non-React hosts | --- ## Related - **[Configuration](./configuration.md)** — all options - **[Chart API](./chart.md)** — `createChart` + Phase 0–5 façade - **[Chart Widget](./chart-widget.md)** — convenience helpers (`setSymbol`, `*AtIndex`, …) - **[Events](./events.md)** — `appCallback` / subscriptions - **[Framework integrations](/framework-integration)** — React, Next, Vue, … - **[Types](./types.md#chartconfig)** — `ChartConfig` / `TradingConfig` / … File: docs/api/helpers.md ==================================== # Helper Functions The GoCharting SDK exports utility helpers for common datafeed tasks. The **only** public helper today is **`getDateRangeForDuration()`** (also on `window.GoChartingSDK` in the UMD build). Other SDK utilities (`createChart`, `setTheme`, overrides, featuresets, etc.) live on the chart/widget APIs — not as standalone helper exports. ## Interactive check In the browser console (or any page that loads the UMD/`@gocharting/chart-sdk`): ```javascript const { getDateRangeForDuration } = window.GoChartingSDK || require("@gocharting/chart-sdk"); console.table( ["1D", "5D", "15D", "1M", "3M", "6M", "1Y", "5Y", "All"].map((d) => { const r = getDateRangeForDuration(d); return { duration: d, interval: r.interval, days: Math.round((r.end_date - r.start_date) / 86400000), }; }), ); ``` ## `getDateRangeForDuration()` Calculates a millisecond start/end window and a **suggested** bar interval for a duration string. Useful when your backend needs explicit `start_date` / `end_date` instead of only a row count. **Signature:** ```typescript function getDateRangeForDuration(duration: DurationString): DateRangeResult type DurationString = | "1D" | "5D" | "15D" | "1M" | "3M" | "6M" | "1Y" | "5Y" | "All" type DateRangeResult = { start_date: number // Start timestamp in milliseconds (epoch UTC) end_date: number // End timestamp in milliseconds (Date.now()) interval: string // Suggested interval for this duration } ``` **Import:** ```javascript import { getDateRangeForDuration } from "@gocharting/chart-sdk"; // UMD: GoChartingSDK.getDateRangeForDuration(...) ``` ### Supported durations (matches the exported helper) | Duration | Approx. lookback | Recommended `interval` | | -------- | ---------------- | ---------------------- | | `"1D"` | 1 day | `"1m"` | | `"5D"` | 5 days | `"15m"` | | `"15D"` | 15 days | `"30m"` | | `"1M"` | 30 days | `"1h"` | | `"3M"` | 90 days | `"2h"` | | `"6M"` | 180 days | `"4h"` | | `"1Y"` | 365 days | `"1D"` | | `"5Y"` | 5 × 365 days | `"1W"` | | `"All"` | 20 × 365 days | `"1M"` | Unknown duration strings fall back to **1 day** / `"1m"`. > [!NOTE] > > **Important notes** > > 1. **Timestamps** — `start_date` / `end_date` are Unix epoch **milliseconds** (UTC). > 2. **`end_date`** — always `Date.now()` at call time. > 3. **Lookbacks** — fixed day multiples (`30 * 86400000` for `"1M"`), not calendar months. > 4. **`interval`** — a suggestion only; you can pass a different resolution to `getBars` / `setInterval`. > 5. **`"All"`** — ~20 years of lookback in the public helper. ## Usage examples ### Basic usage ```javascript import { getDateRangeForDuration } from "@gocharting/chart-sdk"; const { start_date, end_date, interval } = getDateRangeForDuration("1D"); console.log("Start:", new Date(start_date)); // ~1 day ago console.log("End:", new Date(end_date)); // now console.log("Interval:", interval); // "1m" ``` ### Datafeed: use the range when fetching bars `resolveSymbol` must return a full GoCharting security object (`asset_type`, `exchange_info`, precision fields, etc.). The helper is for **time windows**, not symbol metadata. ```javascript import { getDateRangeForDuration } from "@gocharting/chart-sdk"; const myDatafeed = { async getBars(symbolInfo, resolution, periodParams) { const { from, to } = periodParams; const response = await fetch( `/api/bars?symbol=${encodeURIComponent(symbolInfo.symbol)}` + `&from=${from.getTime()}&to=${to.getTime()}` + `&interval=${encodeURIComponent( typeof resolution === "string" ? resolution : resolution?.type || "5m", )}`, ); const data = await response.json(); return { bars: data.map((bar) => ({ time: bar.timestamp, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })), }; }, resolveSymbol(symbolName, onResolve, onError) { const { interval } = getDateRangeForDuration("1M"); // "1h" const parts = String(symbolName).split(":"); const exchange = parts[0] || "BYBIT"; const segment = parts.length >= 3 ? parts[1] : "FUTURE"; const symbol = parts[parts.length - 1] || "BTCUSDT"; const name = `${symbol} perpetual`; const validIntervals = ["1m", "5m", "15m", "1h", "4h", "1D"]; onResolve({ exchange, segment, symbol, name, asset_type: "CRYPTO", source_id: symbol, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.1, display_tick_size: 0.1, volume_size_increment: 1, max_tick_precision: 1, max_volume_precision: 0, quote_currency: "USDT", pair: ["BTC", "USDT"], supports: { footprint: false }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "UTC", hours: Array.from({ length: 7 }, () => ({ open: true })), valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: "crypto", session: "24x7", timezone: "UTC", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); void interval; void onError; }, }; ``` ### Duration selector + `setInterval` ```javascript import { createChart, getDateRangeForDuration } from "@gocharting/chart-sdk"; const durations = ["1D", "5D", "15D", "1M", "3M", "6M", "1Y", "5Y"]; const chart = createChart("#chart", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1h", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", onReady: () => { durations.forEach((duration) => { document .getElementById(`btn-${duration}`) ?.addEventListener("click", () => { const { start_date, end_date, interval } = getDateRangeForDuration(duration); console.log(`Loading ${duration}`, { start_date, end_date, interval, }); chart.setInterval(interval); }); }); }, }); ``` ## Related - [Datafeed API](./datafeed.md) - [Configuration](./configuration.md) - [Types](./types.md) - [Themes](./themes.md) File: docs/api/README.md =================================== # GoCharting SDK - API Reference Complete API documentation for the GoCharting SDK. ## API Documentation ### Core Classes - **[ProfessionalChart](./chart.md)** - Main chart component with full trading capabilities - **[GoCharting React Component](./gocharting-component.md)** - Declarative `` React API - **[Chart Widget API](./chart-widget.md)** - Programmatic control over chart behavior - **[Datafeed API](./datafeed.md)** - Interface for implementing custom data sources - **[CustomFeed](./custom-feed.md)** - Example implementation of datafeed object ### Chart / API surface - **[Widget façade](./chart.md#widget-façade)** — `activeChart` / Phase 0–1E (viewport, shapes, studies, trading lines, events & actions) ### Configuration - **[Chart Configuration](./configuration.md)** - Complete configuration options - **[Theme Configuration](./themes.md)** - Styling and appearance options - **[Trading Configuration](./trading.md)** - Trading-specific settings ### Utilities - **[Enums & literal unions](./enums.md)** - Catalog of real GoCharting union types - **[Event System](./events.md)** - Chart events and callbacks - **[Helper Functions](./helpers.md)** - Utility functions and constants - **[Type Definitions](./types.md)** - TypeScript type definitions ## Quick Reference ### Basic Usage ```javascript import { createChart } from "@gocharting/chart-sdk"; // Create custom datafeed object (no inheritance needed) const myDatafeed = { async getBars(symbolInfo, resolution, periodParams) { // Your data fetching logic return { bars }; }, resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "NASDAQ"; const segment = parts.length >= 3 ? parts[1] : "EQUITY"; const symbol = parts[parts.length - 1] || "AAPL"; const name = symbol; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name, asset_type: "EQUITY", source_id: symbol, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.01, display_tick_size: 0.01, volume_size_increment: 1, max_tick_precision: 2, max_volume_precision: 0, quote_currency: "USD", supports: { footprint: false }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "America/New_York", hours: [ { open: false }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: false }, ], valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: "stock", session: "0930-1600", timezone: "America/New_York", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); }, }; // Initialize chart with simplified API const chart = createChart("#chart", { symbol: "AAPL", interval: "1D", datafeed: myDatafeed, // Pass the object directly licenseKey: "YOUR_LICENSE_KEY", trading: { enableTrading: true, // Enable trading features }, theme: "dark", }); ``` ### React (Declarative Component) ```jsx import { GoCharting } from "@gocharting/chart-sdk"; ; ``` See **[GoCharting React Component](./gocharting-component.md)** for the full props reference. ### Essential Methods ```javascript // Chart control chart.setSymbol('NASDAQ:MSFT'); chart.setInterval('1h'); chart.setTheme('dark'); chart.resize(800, 600); // Trading data chart.setBrokerAccounts({ accountList: [...], orderBook: [...], tradeBook: [...], positions: [...] }); // Cleanup chart.destroy(); ``` Events (`onReady`, `onError`, `appCallback`) are configured in the `createChart` config, not as instance methods — see the [Events API](./events.md). ## Method Categories ### Lifecycle - `destroy()` - Clean up and destroy the chart - `isDestroyed()` - Check whether the chart is destroyed - `getChartInstance()` - Get the underlying chart instance ### Widget façade - `activeChart()` / `chart(index)` — per-chart `IChartApi` (Phase 0–1E) - `chartsCount()` / `activeChartIndex()` / `setActiveChart(index)` - `subscribe` / `unsubscribe` / `getAllCharts()` ### Chart Control - `setSymbol(symbol)` - Change the displayed symbol - `setInterval(interval)` - Change the time interval - `setTheme(theme)` - Change chart theme - `setChartType(chartType)` - Change the chart type - `resize(width, height)` - Resize the chart - `goToDate(date)` - Jump to a specific date - `setTimezone(timezone)` / `getCurrentTimezone()` / `getTimezonePresets()` - Timezone control ### Objects, Settings & State - `addIndicator(indicator)` / `addDrawing(drawing)` / `deleteObject(objectId)` - Manage chart objects - `updateSettings(settings)` - Update chart settings - `getChartState()` / `setChartState(state)` - Persist and restore chart state - `...AtIndex(...)` variants - Target a specific chart in multichart layouts ### Templates - `saveTemplate(name)` / `applyTemplate(template)` / `getTemplates()` / `deleteTemplate(id)` - Indicator templates ### Trading Data - `setBrokerAccounts(data)` - Set trading account data - `updatePositions(positions)` - Merge partial position updates - `resubscribeAll(idToken)` - Resubscribe after reconnection See the **[Chart API](./chart.md)** for the complete method reference with examples. ## Configuration Options ### Chart Configuration ```javascript { // Required datafeed: object, symbol: string, interval: string, licenseKey: string, // Common options theme: 'dark' | 'light', locale: string, autosize: boolean, width: string | number, height: string | number, trading: { enableTrading: boolean, showOpenOrders: boolean, showPositions: boolean }, disableSearch: boolean, disableCompare: boolean, autoSave: boolean, // Callbacks appCallback: (event) => {}, onReady: (chartInstance) => {}, onError: (error) => {} } ``` See the **[Configuration API](./configuration.md)** for the complete `ChartConfig` reference (including `defaultInitialChartConfig`, `themeColor`, `contextMenu`, `popups`, `favourite`, `isNativeApp`, `touchMode`, and more). ### Datafeed Interface ```javascript // Plain object — no class inheritance required const myDatafeed = { // Required methods async getBars(symbolInfo, resolution, periodParams) {}, resolveSymbol(symbolName, onResolve, onError) {}, // Optional methods searchSymbols(userInput, exchange, symbolType, onResultReadyCallback) {}, subscribeTicks(symbolInfo, resolution, onRealtimeCallback, subscriberUID, onResetCacheNeededCallback) {}, unsubscribeTicks(subscriberUID) {}, getMarks(symbolInfo, startDate, endDate, onDataCallback, resolution) {}, getTimescaleMarks(symbolInfo, from, to, onDataCallback, resolution) {}, destroy() {}, }; ``` ## Common Use Cases ### 1. Basic Chart Setup ```javascript const chart = createChart("#chart", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: myDatafeed, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", }); ``` ### 2. Custom Data Source ```javascript const apiDatafeed = { async getBars(symbolInfo, resolution, periodParams) { const response = await fetch(`/api/bars?symbol=${symbolInfo.name}`); const data = await response.json(); return { bars: data }; }, resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "BYBIT"; const segment = parts.length >= 3 ? parts[1] : "FUTURE"; const symbol = parts[parts.length - 1] || "BTCUSDT"; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name: "Demo Symbol", asset_type: "CRYPTO", source_id: symbol, pair: { from: "BTC", to: "USDT" }, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.1, display_tick_size: 1, volume_size_increment: 0.001, max_tick_precision: 1, max_volume_precision: 3, quote_currency: "USDT", future_type: "PERP", supports: { footprint: true }, exchange_info: { name: exchange.toLowerCase(), code: exchange, zone: "UTC", hours: Array.from({ length: 7 }, () => ({ open: true })), valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: "Demo Symbol", type: "crypto", session: "24x7", timezone: "UTC", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); }, }; ``` ### 3. Trading Integration ```javascript chart.setBrokerAccounts({ accountList: [{ id: "123", balance: 10000 }], orderBook: [], positions: [], }); ``` ## Data Formats ### Bar Data Format ```javascript { time: 1609459200000, // Unix timestamp in milliseconds open: 100.50, high: 102.75, low: 99.25, close: 101.80, volume: 1500000 } ``` ### Symbol Info Format ```javascript { // Basic Information exchange: 'BYBIT', segment: 'FUTURE', symbol: 'BTCUSDT', name: 'BTC / USDT PERPETUAL FUTURES', asset_type: 'CRYPTO', source_id: 'BTCUSDT', // Pair Information pair: { from: 'BTC', to: 'USDT' }, // Trading Information tradeable: true, is_index: false, is_formula: false, // Data Feed Information delay_seconds: 0, data_status: 'streaming', // Price & Volume Precision tick_size: 0.1, display_tick_size: 1, volume_size_increment: 0.001, max_tick_precision: 1, max_volume_precision: 3, quote_currency: 'USDT', // Exchange Information exchange_info: { name: 'bybit', code: 'BYBIT', zone: 'UTC', valid_intervals: ['1m', '5m', '15m', '30m', '1h', '4h', '1D', '1W', '1M'] }, // Legacy/Compatibility Fields ticker: 'BTCUSDT', full_name: 'BYBIT:FUTURE:BTCUSDT', description: 'BTC / USDT PERPETUAL FUTURES', type: 'crypto', session: '24x7', timezone: 'UTC', has_intraday: true, supported_resolutions: ['1', '5', '15', '30', '60', '240', '1D'] } ``` ## Related Documentation - **[Installation Guide](../guides/installation.md)** - Setup instructions - **[Configuration Guide](../guides/configuration.md)** - Detailed configuration - **[Examples](../examples/)** - Working code examples - **[Tutorials](../tutorials/)** - Step-by-step guides ## Need Help? - **Email**: admin@gocharting.com - **Documentation**: [gocharting.com/sdk/docs](https://gocharting.com/sdk/docs) - **GitHub**: [github.com/gocharting/sdk](https://github.com/gocharting/sdk) - **Discord**: [gocharting.com/discord](https://gocharting.com/discord) --- _For detailed method documentation, click on the specific API sections above._ File: docs/api/themes.md =================================== # Themes API Control chart light/dark mode with `theme` / `setTheme()`, and optionally set an exact background hex via `themeColor`. ## Interactive lab Open [`examples/themes-lab.html`](../../examples/themes-lab.html) (from the SDK package root: `npm run serve`, then [http://127.0.0.1:8080/examples/themes-lab.html](http://127.0.0.1:8080/examples/themes-lab.html)) to verify: - Construct with `theme: "light"` / `"dark"` - Runtime `setTheme("light" | "dark")` - Optional `themeColor` hex (including custom colors) - Explicit construct options vs `localStorage` `gocharting-theme-color` - Live demo WebSocket bars (`wss://gocharting.com/sdk/ws`) ## Preferred API: `theme` Pass `"light"` or `"dark"` to `createChart()` (or the `` component). When `themeColor` is omitted, the SDK maps: | `theme` | Default `themeColor` | | ------- | -------------------- | | `"light"` | `#ffffff` | | `"dark"` | `#22292f` | ```javascript import { createChart } from "@gocharting/chart-sdk"; const chart = createChart("#chart", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", theme: "dark", }); ``` ## Runtime: `setTheme()` ```typescript chart.setTheme("light" | "dark"): void ``` ```javascript chart.setTheme("dark"); ``` Toggle example: ```javascript let mode = "light"; document.getElementById("theme-toggle").addEventListener("click", () => { mode = mode === "light" ? "dark" : "light"; chart.setTheme(mode); }); ``` ## Optional: `themeColor` (hex background) `themeColor` is a **CSS hex color** for the app chrome (e.g. `#22292f`, `#570f0f`). It is **not** `"light"` or `"dark"` — use `theme` for that. Any `#RRGGBB` value is supported. Built-in tuned palettes also exist for: `#ffffff`, `#22292f`, `#e3342f`, `#f6993f`, `#ffed4a`, `#38c172`, `#4dc0b5`, `#3490dc`, `#6574cd`, `#9561e2`, `#f66d9b`. ```javascript createChart("#chart", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", themeColor: "#570f0f", }); ``` Rules of thumb: - Prefer **`theme` only** for normal light/dark switching. - Use **`themeColor`** when you need a specific background hex. - Explicit `theme` / `themeColor` on `createChart` win over a saved `gocharting-theme-color` in `localStorage`. - `setTheme("light"|"dark")` switches the named mode at runtime (maps to `#ffffff` / `#22292f`). ## Construct-time options (advanced) | Option | Role | | ------ | ---- | | `custom_css_url` | Inject an external stylesheet | | `custom_themes` | CSS variable / token map applied at construct time | | `toolbar_bg` | Toolbar background CSS variable | | `overrides` / `applyOverrides` | Chart appearance (canvas), not the same as `theme` | See [Configuration](./configuration.md) for full option lists. ## System preference ```javascript const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; const chart = createChart("#chart", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", theme: prefersDark ? "dark" : "light", }); window .matchMedia("(prefers-color-scheme: dark)") .addEventListener("change", (e) => { chart.setTheme(e.matches ? "dark" : "light"); }); ``` ## Reference | Option / method | Type | Description | | --------------- | ---- | ----------- | | `theme` | `"light" \\| "dark"` | Named theme (preferred) | | `themeColor` | `string` (hex/CSS color) | Optional background override — **not** `"light"`/`"dark"` | | `setTheme(mode)` | `(mode: "light" \\| "dark") => void` | Switch theme after create | ## Related - [Configuration](./configuration.md) - [Chart API](./chart.md) - [GoCharting component](./gocharting-component.md) File: docs/api/trading.md ==================================== # Trading API The GoCharting SDK provides comprehensive trading integration capabilities through the `trading.enableTrading` configuration option and trading event callbacks. Literal unions used by trading payloads (`OrderType`, `OrderSide`, `OrderStatus`, `TimeInForce`, `ExecutionDirection`, `AppCallbackEventType`) are cataloged under **[Enums & literal unions](./enums.md#trading)**. ## Quick Start ### Enable Trading Features ```javascript const chart = GoChartingSDK.createChart("#chart-container", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", trading: { enableTrading: true, // Enable trading features }, appCallback: handleTradingEvents, // Handle trading events }); ``` ## Configuration ### trading.enableTrading Parameter | Parameter | Type | Default | Description | | ------------------------ | --------- | ------- | ------------------------------------ | | `trading.enableTrading` | `boolean` | `false` | Enable trading features in the chart | When `trading.enableTrading: true`, the following features become available: - ** Right-click Context Menu** - Buy/Sell options on chart right-click - ** CrossHair Trading Menu** - Trading options when hovering over prices - ** Settings > Trading Tab** - Trading configuration panel - ** Trading Event Callbacks** - Handle trading events through `appCallback` ## Trading Events ### Event Handler ```javascript function handleTradingEvents(eventType, eventData) { switch (eventType) { case "PLACE_ORDER": console.log("Place order:", eventData); // Handle order placement break; case "CANCEL_ORDER": console.log("Cancel order:", eventData); // Handle order cancellation break; case "MODIFY_ORDER": console.log("Modify order:", eventData); // Handle order modification break; default: console.log("Trading event:", eventType, eventData); } } ``` ### Event Types | Event Type | Description | Event Data | | ----------------- | ------------------------------------------------------------ | -------------------------------------------------------- | | `PLACE_ORDER` | User places a new order or exits a position (via X button) | `{ symbol, side, quantity, price }` | | `CANCEL_ORDER` | User cancels an existing order | `{ orderId, symbol }` | | `MODIFY_ORDER` | User modifies an existing order | `{ orderId, quantity, price, task, update, updateType }` | | `MODIFY_POSITION` | User modifies an existing position (TP/SL via chart buttons) | `{ position: { ...positionData, update, updateType } }` | ## Order Modification Events The `MODIFY_ORDER` event provides detailed information about what type of modification is being performed. The event structure includes: ```javascript { "order": { "orderId": "ORDER_123", "task": "modify", "update": "SHAPE_MODIFY", // or "DELETE_SL", "DELETE_TP" "updateType": "TAKE_PROFIT", // or "STOP_LOSS", "LIMIT_PRICE" "takeProfit": 125000, // New take profit value "stopLoss": 110000, // New stop loss value "price": 115500 // New limit price } } ``` ### Modification Types #### **Delete Operations** - **`update: "DELETE_SL"`** - Delete stop loss from order - **`update: "DELETE_TP"`** - Delete take profit from order #### **Shape Modifications** - **`update: "SHAPE_MODIFY", updateType: "TAKE_PROFIT"`** - Update take profit price - **`update: "SHAPE_MODIFY", updateType: "STOP_LOSS"`** - Update stop loss price - **`update: "SHAPE_MODIFY", updateType: "LIMIT_PRICE"`** - Update order limit price #### **Standard Modifications** - **`update: "STANDARD"`** - Standard price/quantity modifications ### Handling Order Modifications ```javascript function handleTradingEvents(eventType, eventData) { switch (eventType) { case "MODIFY_ORDER": const order = eventData.order || eventData; switch (order.update) { case "DELETE_SL": // Remove stop loss from order updateOrder(order.orderId, { stopLoss: null }); break; case "DELETE_TP": // Remove take profit from order updateOrder(order.orderId, { takeProfit: null }); break; case "SHAPE_MODIFY": if (order.updateType === "TAKE_PROFIT") { // Update take profit price updateOrder(order.orderId, { takeProfit: order.takeProfit, }); } else if (order.updateType === "STOP_LOSS") { // Update stop loss price updateOrder(order.orderId, { stopLoss: order.stopLoss, }); } else if (order.updateType === "LIMIT_PRICE") { // Update limit price updateOrder(order.orderId, { price: order.price }); } break; default: // Handle standard modifications updateOrder(order.orderId, { price: order.price, quantity: order.quantity || order.size, }); break; } break; } } ``` ## Position Modification Events The `MODIFY_POSITION` event is triggered when users interact with position Stop Loss and Take Profit buttons on the chart. This event provides detailed information about what type of modification is being performed. **Event Structure:** ```javascript { "position": { // All original position fields "id": "POS_001", "productId": "BYBIT:FUTURE:BTCUSDT", "size": 0.05, "price": 49500, // ... other position fields ... // Modification metadata "update": "SHAPE_MODIFY", "updateType": "NEW_TAKE_PROFIT", // or other updateType values // Updated TP/SL values "takeProfit": 51000, "stopLoss": 48000 } } ``` ### Position Modification Types #### **Delete Operations** - **`update: "SHAPE_MODIFY", updateType: "DELETE_STOP_LOSS"`** - Delete stop loss from position - **`update: "SHAPE_MODIFY", updateType: "DELETE_TAKE_PROFIT"`** - Delete take profit from position #### **Shape Modifications** - **`update: "SHAPE_MODIFY", updateType: "NEW_TAKE_PROFIT"`** - Add new take profit (first time) - **`update: "SHAPE_MODIFY", updateType: "TAKE_PROFIT"`** - Modify existing take profit price - **`update: "SHAPE_MODIFY", updateType: "NEW_STOP_LOSS"`** - Add new stop loss (first time) - **`update: "SHAPE_MODIFY", updateType: "STOP_LOSS"`** - Modify existing stop loss price ### Handling Position Modifications ```javascript function handleTradingEvents(eventType, eventData) { switch (eventType) { case "MODIFY_POSITION": const position = eventData.position; if (position.update === "SHAPE_MODIFY") { switch (position.updateType) { case "NEW_STOP_LOSS": // User added a stop loss for the first time console.log("New Stop Loss:", position.stopLoss); await brokerAPI.addStopLoss(position.id, position.stopLoss); break; case "STOP_LOSS": // User modified existing stop loss console.log("Updated Stop Loss:", position.stopLoss); await brokerAPI.updateStopLoss(position.id, position.stopLoss); break; case "NEW_TAKE_PROFIT": // User added a take profit for the first time console.log("New Take Profit:", position.takeProfit); await brokerAPI.addTakeProfit( position.id, position.takeProfit ); break; case "TAKE_PROFIT": // User modified existing take profit console.log("Updated Take Profit:", position.takeProfit); await brokerAPI.updateTakeProfit( position.id, position.takeProfit ); break; case "DELETE_TAKE_PROFIT": // User removed take profit console.log("Deleted Take Profit"); await brokerAPI.removeTakeProfit(position.id); break; case "DELETE_STOP_LOSS": // User removed stop loss console.log("Deleted Stop Loss"); await brokerAPI.removeStopLoss(position.id); break; } // Update the chart with the modified position updateChartPositions(); } break; case "MODIFY_ORDER": // ... handle order modifications ... break; } } ``` ## Integration Examples ### Basic Trading Integration ```javascript // Create chart with trading enabled const chart = GoChartingSDK.createChart("#trading-chart", { symbol: "NASDAQ:AAPL", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", trading: { enableTrading: true, }, appCallback: (eventType, eventData) => { // Handle all trading events console.log("Trading event:", eventType, eventData); }, }); ``` ### Advanced Trading with Broker Integration ```javascript // Advanced trading setup with broker accounts const chart = GoChartingSDK.createChart("#advanced-trading", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", trading: { enableTrading: true, }, appCallback: handleTradingEvents, }); // Set broker accounts for realistic trading experience chart.setBrokerAccounts({ accountList: [{ id: "demo-account", name: "Demo Account", balance: 10000 }], orderBook: [], tradeBook: [], positions: [], }); function handleTradingEvents(eventType, eventData) { switch (eventType) { case "PLACE_ORDER": // Send order to your broker API placeBrokerOrder(eventData); break; case "CANCEL_ORDER": // Cancel order through broker API cancelBrokerOrder(eventData.orderId); break; } } ``` ## UI Features ### Context Menu Options When `trading.enableTrading: true`, users can: 1. **Right-click on chart** → See "Buy" and "Sell" options 2. **Hover over price levels** → See trading options in CrossHair menu 3. **Access Settings > Trading** → Configure trading preferences ### Trading Tab in Settings The Trading tab provides configuration options for: - Order line display settings - Position visualization - Trading notifications - Default order parameters ## Trading Panel Components ### Order Entry Form The trading panel includes a comprehensive order entry form with the following fields: ```html
``` ### Trading Actions #### **Buy/Sell Orders** - **Market Orders**: Execute immediately at current LTP (Last Traded Price) - **Limit Orders**: Added to orderbook as "open" status - **Stop Loss/Take Profit**: Optional risk management levels #### **Reset Broker Functionality** The Reset Broker button provides a clean way to reset the demo environment: ```javascript // Reset Broker Implementation function resetBroker() { // Clear all broker data arrays this.currentOrderBook = []; this.currentTradeBook = []; this.currentPositions = []; // Update chart with empty broker data this.updateChartBrokerData(); console.log( " Broker data reset - All orders, trades, and positions cleared", ); } ``` **What gets reset:** - **OrderBook**: All pending and filled orders cleared - **TradeBook**: All trade history cleared - **Positions**: All open positions cleared - **Chart Display**: All order lines, position markers removed ### Order Type Behavior #### **Market Orders** ```javascript // Market order execution { orderType: "market", price: 0, // Uses current LTP status: "filled", // Immediately filled // Creates position and trade immediately } ``` #### **Limit Orders** ```javascript // Limit order placement { orderType: "limit", price: 115500, // User-specified price status: "open", // Pending execution // Waits for market price to reach limit price } ``` ## Broker Data Structures ### setBrokerAccounts Method The `setBrokerAccounts()` method is the primary way to provide trading data to the chart. This method accepts an object with four main arrays that represent your broker's current state: ```javascript chartInstance.setBrokerAccounts({ accountList: [...], // Account information orderBook: [...], // Active orders tradeBook: [...], // Trade history positions: [...] // Current positions }); ``` **Method Signature:** ```typescript setBrokerAccounts(data: BrokerAccountData): void interface BrokerAccountData { accountList: Account[]; orderBook: Order[]; tradeBook: Trade[]; positions: Position[]; } ``` ### updatePositions Method The `updatePositions()` method allows you to update specific fields on existing positions without triggering a full `setBrokerAccounts` rebuild. This is useful for frequently-changing fields like `pnlMultiplier` (cross-currency conversion rates), `bid`, `ask`, or `ltp`. ```typescript updatePositions(positions: Array & Pick>): void ``` Each object in the array must include a `key` field to identify which position to update. Only the provided fields are merged; other fields remain unchanged. ```javascript // Update pnlMultiplier for cross-currency positions when FX rate changes chartInstance.updatePositions([ { key: "pos-1", pnlMultiplier: 0.0285 }, // Updated TRY/EUR rate { key: "pos-2", pnlMultiplier: 1.0842 }, // Updated USD/EUR rate ]); // Update bid/ask/ltp on positions chartInstance.updatePositions([ { key: "pos-1", bid: 66.03, ask: 66.07, ltp: 66.05 }, ]); ``` ### Account List Structure The `accountList` array contains information about trading accounts: ```javascript accountList: [ { account_id: "DEMO_001", // Unique account identifier AccountID: "DEMO_001", // Alternative account ID field AccountType: "Demo Trading", // Account type description label: "Demo Account (Trading)", // Display label for UI currency: "USD", // Account base currency balance: 100000, // Account balance equity: 100000, // Account equity margin: 0, // Used margin freeMargin: 100000, // Available margin }, ]; ``` **Account Fields:** | Field | Type | Required | Description | | ------------- | -------- | -------- | ------------------------------------------ | | `account_id` | `string` | | Unique account identifier | | `AccountID` | `string` | | Alternative account ID (for compatibility) | | `AccountType` | `string` | | Account type (Demo, Live, Paper) | | `label` | `string` | | Display name for the account | | `currency` | `string` | | Base currency (USD, EUR, etc.) | | `balance` | `number` | | Current account balance | | `equity` | `number` | | Account equity value | | `margin` | `number` | | Used margin amount | | `freeMargin` | `number` | | Available margin for trading | ### Order Book Structure The `orderBook` array contains all active (pending) orders: ```javascript orderBook: [ { orderId: "ORDER_001", // Unique order identifier datetime: new Date(), // Order creation date timeStamp: new Date().getTime(), // Order timestamp (ms) lastTradeTimestamp: null, // Last trade timestamp status: "open", // Order status price: 50000, // Order price size: 0.1, // Order quantity productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier remainingSize: 0.1, // Remaining quantity orderType: "limit", // Order type side: "buy", // Order side cost: null, // Order cost trades: [], // Associated trades fee: { // Fee information currency: "USDT", cost: 0.0, rate: 0.0, }, info: {}, // Additional broker info fillPrice: null, // Fill price (if any) avgFillPrice: null, // Average fill price filledSize: 0, // Filled quantity modifiedAt: null, // Last modification time exchange: "BYBIT", // Exchange name symbol: "BTCUSDT", // Base symbol takeProfit: null, // Take profit price stopLoss: null, // Stop loss price isGC: true, // GoCharting flag paperTraderKey: null, // Paper trading key key: "demo-BYBIT:FUTURE:BTCUSDT-ORDER_001", // Unique key validity: "DAY", // Order validity commissions: 0, // Commission amount broker: "demo", // Broker identifier stopPrice: null, // Stop price (for stop orders) productType: "FUTURE", // Product type rejReason: null, // Rejection reason security: { // Security information symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, userTag: null, // User-defined tag }, ]; ``` **Order Fields:** | Field | Type | Required | Description | | --------------- | -------- | -------- | -------------------------------------------------- | | `orderId` | `string` | | Unique order identifier | | `datetime` | `Date` | | Order creation date | | `timeStamp` | `number` | | Order timestamp in milliseconds | | `status` | `string` | | Order status: "open", "filled", "cancelled" | | `price` | `number` | | Order price (0 for market orders) | | `size` | `number` | | Order quantity | | `productId` | `string` | | Full symbol identifier | | `remainingSize` | `number` | | Remaining unfilled quantity | | `orderType` | `string` | | Order type: "limit", "market", "stop", "stopLimit" | | `side` | `string` | | Order side: "buy", "sell" | | `exchange` | `string` | | Exchange name | | `symbol` | `string` | | Base symbol name | | `broker` | `string` | | Broker identifier | | `productType` | `string` | | Product type: "FUTURE", "SPOT", "OPTION" | | `security` | `object` | | Security information object | | `fee` | `object` | | Fee structure | | `takeProfit` | `number` | | Take profit price | | `stopLoss` | `number` | | Stop loss price | | `stopPrice` | `number` | | Stop trigger price | ### Trade Book Structure The `tradeBook` array contains the history of executed trades: ```javascript tradeBook: [ { tradeId: "TRADE_001", // Unique trade identifier orderId: "ORDER_FILLED", // Associated order ID datetime: new Date(Date.now() - 3600000), // Trade execution date timeStamp: new Date(Date.now() - 3600000).getTime(), // Trade timestamp lastTradeTimestamp: null, // Last trade timestamp status: "filled", // Trade status price: 49500, // Execution price tradeSize: 0.05, // Trade quantity tradeValue: 2475, // Trade value (price * size) productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier orderType: "Market", // Original order type side: "buy", // Trade side cost: null, // Trade cost takerOrMaker: null, // Taker/Maker flag isGC: true, // GoCharting flag fee: { // Fee information currency: "USDT", cost: 2.475, // Fee amount rate: 0.001, // Fee rate }, info: {}, // Additional broker info broker: "demo", // Broker identifier paperTraderKey: null, // Paper trading key key: "demo-BYBIT:FUTURE:BTCUSDT-TRADE_001", // Unique key type: "tradeExecuted", // Trade type remainingSize: "0", // Remaining order size orderDetails: { // Original order details orderId: "ORDER_FILLED", orderType: "market", }, triggerLogicPending: false, // Trigger logic flag spread: 0, // Bid-ask spread productType: "FUTURE", // Product type security: { // Security information symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, }, ]; ``` **Trade Fields:** | Field | Type | Required | Description | | -------------- | -------- | -------- | ---------------------------------------- | | `tradeId` | `string` | | Unique trade identifier | | `orderId` | `string` | | Associated order ID | | `datetime` | `Date` | | Trade execution date | | `timeStamp` | `number` | | Trade timestamp in milliseconds | | `status` | `string` | | Trade status: "filled", "partial" | | `price` | `number` | | Execution price | | `tradeSize` | `number` | | Trade quantity | | `tradeValue` | `number` | | Trade value (price × quantity) | | `productId` | `string` | | Full symbol identifier | | `orderType` | `string` | | Original order type | | `side` | `string` | | Trade side: "buy", "sell" | | `broker` | `string` | | Broker identifier | | `productType` | `string` | | Product type: "FUTURE", "SPOT", "OPTION" | | `security` | `object` | | Security information object | | `fee` | `object` | | Fee structure | | `takerOrMaker` | `string` | | "taker" or "maker" | | `spread` | `number` | | Bid-ask spread at execution | ### Positions Structure The `positions` array contains current open positions: ```javascript positions: [ { size: 0.05, // Net position size size_currency: 0.05, // Position quantity price: 49500, // Average entry price amount: 2475, // Position value openPosVal: 0, // Open position value closedPosVal: 0, // Closed position value productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier broker: "demo", // Broker identifier isGC: true, // GoCharting flag paperTraderKey: null, // Paper trading key key: "demo-BYBIT:FUTURE:BTCUSDT-POS_001", // Unique key productType: "FUTURE", // Product type currency: "USDT", // Position currency id: "POS_001", // Position ID positions: { // Detailed position data "demo-BYBIT:FUTURE:BTCUSDT-FUTURE": { positionId: "demo-BYBIT:FUTURE:BTCUSDT-FUTURE", positionFlag: true, // Position active flag security: { // Security information symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, positionStatus: "open", // Position status entry: 49500, // Entry price exit: null, // Exit price (if closed) size: 0.05, // Position size positionSize: 0.05, // Position size (duplicate) transaction_type: "real", // Transaction type trades: [], // Associated trades broker: "demo", // Broker identifier currency: "USDT", // Position currency productId: "BYBIT:FUTURE:BTCUSDT", // Full symbol identifier ticker: "BYBIT:FUTURE:BTCUSDT", // Ticker symbol created: null, // Creation timestamp symbol: "BTCUSDT", // Base symbol exchange: "BYBIT", // Exchange name segment: "FUTURE", // Market segment underlying: "BYBIT:FUTURE:BTCUSDT", // Underlying asset series: null, // Series (for options) strike: null, // Strike price (for options) optionType: null, // Option type (for options) expiration_timestamp: null, // Expiration (for options) strategyId: "1000", // Strategy ID strategyType: "1000", // Strategy type strategyLabel: "Real", // Strategy label strategyColor: "#000000", // Strategy color strategyFlag: true, // Strategy active flag strategyStatus: "open", // Strategy status stratgeyFormulae: null, // Strategy formula fee: { // Fee information currency: "USDT", cost: 0.0, rate: 0.0, }, side: "buy", // Position side modified: new Date().getTime(), // Last modified timestamp productType: "FUTURE", // Product type rpnl: 0, // Realized PnL totalBuyQty: 0.05, // Total buy quantity avgBuyPrice: 49500, // Average buy price totalSellQty: 0, // Total sell quantity avgSellPrice: 0, // Average sell price }, }, underlying: "BYBIT:FUTURE:BTCUSDT", // Underlying asset unPnl: 25, // Unrealized PnL rPnl: 0, // Realized PnL pnl: 25, // Total PnL pnlMultiplier: 1, // PnL multiplier security: { // Security information symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, symbol: "BTCUSDT", // Base symbol segment: "FUTURE", // Market segment exchange: "BYBIT", // Exchange name }, ]; ``` **Position Fields:** | Field | Type | Required | Description | | ---------------------- | --------- | -------- | ----------------------------------------------------------- | | `size` | `number` | | Net position size (positive = long, negative = short) | | `size_currency` | `number` | | Position quantity in base currency | | `price` | `number` | | Average entry price | | `amount` | `number` | | Position value (price × quantity) | | `productId` | `string` | | Full symbol identifier | | `broker` | `string` | | Broker identifier | | `productType` | `string` | | Product type: "FUTURE", "SPOT", "OPTION" | | `currency` | `string` | | Position currency | | `id` | `string` | | Position identifier | | `underlying` | `string` | | Underlying asset identifier | | `unPnl` | `number` | | Unrealized profit/loss | | `rPnl` | `number` | | Realized profit/loss | | `pnl` | `number` | | Total profit/loss | | `security` | `object` | | Security information object | | `symbol` | `string` | | Base symbol name | | `segment` | `string` | | Market segment | | `exchange` | `string` | | Exchange name | | `positions` | `object` | | Detailed position breakdown | | `pnlMultiplier` | `number` | | PnL calculation multiplier (use `updatePositions` for live updates) | | `ltp` | `number` | | Last traded price — used as market price before first tick | | `showStopLossButton` | `boolean` | | Enable Stop Loss button on position line (default: false) | | `showTakeProfitButton` | `boolean` | | Enable Take Profit button on position line (default: false) | | `stopLoss` | `number` | | Current Stop Loss price for the position | | `takeProfit` | `number` | | Current Take Profit price for the position | ### Position Modification with Stop Loss and Take Profit Buttons When you set `showStopLossButton: true` and/or `showTakeProfitButton: true` on a position object, interactive buttons will appear on the position line in the chart, allowing users to add or modify Stop Loss and Take Profit levels directly from the chart. **Example Position with TP/SL Buttons:** ```javascript positions: [ { size: 0.05, price: 49500, productId: "BYBIT:FUTURE:BTCUSDT", broker: "demo", // ... other required fields ... // Enable interactive TP/SL buttons showStopLossButton: true, // Shows red S/L button showTakeProfitButton: true, // Shows blue T/P button // Current TP/SL values (can be null if not set) stopLoss: null, // No stop loss set yet takeProfit: null, // No take profit set yet }, ]; ``` **When users interact with these buttons, you'll receive a `MODIFY_POSITION` event:** ```javascript appCallback: (eventType, eventData) => { if (eventType === "MODIFY_POSITION") { const position = eventData.position; // Check the modification type if (position.update === "SHAPE_MODIFY") { // The position shape (TP/SL) was modified switch (position.updateType) { case "NEW_STOP_LOSS": // User clicked the red S/L button and placed a NEW Stop Loss console.log("New Stop Loss added:", position.stopLoss); // This is the FIRST time a stop loss is being added break; case "STOP_LOSS": // User modified an EXISTING Stop Loss price console.log("Stop Loss updated:", position.stopLoss); // There was already a stop loss, user changed the price break; case "NEW_TAKE_PROFIT": // User clicked the blue T/P button and placed a NEW Take Profit console.log("New Take Profit added:", position.takeProfit); // This is the FIRST time a take profit is being added break; case "TAKE_PROFIT": // User modified an EXISTING Take Profit price console.log("Take Profit updated:", position.takeProfit); // There was already a take profit, user changed the price break; case "DELETE_TAKE_PROFIT": // User deleted the Take Profit console.log("Take Profit removed"); // position.takeProfit will be null break; case "DELETE_STOP_LOSS": // User deleted the Stop Loss console.log("Stop Loss removed"); // position.stopLoss will be null break; } // Update your broker system with the new TP/SL values updatePositionTPSL(position); } } }; ``` **Position Modification Event Structure:** | Field | Type | Description | | ------------------------ | ---------------- | ----------------------------------------------- | | `update` | `string` | Always `"SHAPE_MODIFY"` for TP/SL modifications | | `updateType` | `string` | Type of modification (see table below) | | `stopLoss` | `number \| null` | New Stop Loss price (or null if deleted) | | `takeProfit` | `number \| null` | New Take Profit price (or null if deleted) | | ...other position fields | | All original position fields are included | **Update Types for Position Modifications:** | updateType | Description | When it occurs | | ---------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------- | | `"NEW_STOP_LOSS"` | First-time Stop Loss placement | User clicks red S/L button and sets a stop loss for the first time (previous stopLoss was 0 or null) | | `"STOP_LOSS"` | Existing Stop Loss modification | User drags or modifies an existing stop loss line (previous stopLoss > 0) | | `"NEW_TAKE_PROFIT"` | First-time Take Profit placement | User clicks blue T/P button and sets a take profit for the first time (previous takeProfit was 0 or null) | | `"TAKE_PROFIT"` | Existing Take Profit modification | User drags or modifies an existing take profit line (previous takeProfit > 0) | | `"DELETE_TAKE_PROFIT"` | Take Profit deletion | User removes the take profit from the position | | `"DELETE_STOP_LOSS"` | Stop Loss deletion | User removes the stop loss from the position | **Complete Example:** ```javascript // Set up positions with TP/SL buttons enabled chart.setBrokerAccounts({ accountList: [ { id: "demo", name: "Demo Account", balance: 10000, currency: "USDT" } ], orderBook: [], tradeBook: [], positions: [ { size: 0.05, size_currency: 0.05, price: 49500, amount: 2475, productId: "BYBIT:FUTURE:BTCUSDT", broker: "demo", productType: "FUTURE", currency: "USDT", id: "POS_001", underlying: "BYBIT:FUTURE:BTCUSDT", unPnl: 25, rPnl: 0, pnl: 25, security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, symbol: "BTCUSDT", segment: "FUTURE", exchange: "BYBIT", // Enable TP/SL buttons showStopLossButton: true, showTakeProfitButton: true, // Initial TP/SL values stopLoss: null, // No stop loss yet takeProfit: null, // No take profit yet } ] }); // Handle position modifications function handleTradingEvents(eventType, eventData) { if (eventType === "MODIFY_POSITION") { const position = eventData.position; if (position.update === "SHAPE_MODIFY") { console.log("Position modified:", { updateType: position.updateType, stopLoss: position.stopLoss, takeProfit: position.takeProfit, productId: position.productId }); // Send to your broker API await brokerAPI.updatePositionTPSL({ positionId: position.id, stopLoss: position.stopLoss, takeProfit: position.takeProfit, updateType: position.updateType }); // Update the chart with the new position data const updatedPositions = currentPositions.map(p => p.id === position.id ? { ...p, stopLoss: position.stopLoss, takeProfit: position.takeProfit } : p ); chart.setBrokerAccounts({ accountList: currentAccounts, orderBook: currentOrders, tradeBook: currentTrades, positions: updatedPositions }); } } } ``` ### Exiting Positions with the X Button Positions displayed on the chart include an **X button** that allows users to quickly exit (close) the position. When a user clicks this button, the chart will trigger a `PLACE_ORDER` event with the following characteristics: **Exit Order Behavior:** - **Side**: Opposite of the position side - Long position (buy) → Exit order has `side: "sell"` - Short position (sell) → Exit order has `side: "buy"` - **Size/Quantity**: Same as the position size (to fully close the position) - **Order Type**: Typically a market order for immediate execution **Example - Exiting a Long Position:** ```javascript // Original position const position = { size: 0.05, // Long position (positive size) price: 49500, productId: "BYBIT:FUTURE:BTCUSDT", // ... other fields }; // When user clicks X button, you receive: appCallback: (eventType, eventData) => { if (eventType === "PLACE_ORDER") { console.log("Exit order:", eventData); // { // side: "sell", // Opposite of long position // size: 0.05, // Same as position size // orderType: "market", // Market order for immediate exit // productId: "BYBIT:FUTURE:BTCUSDT", // // ... other order fields // } } }; ``` **Example - Exiting a Short Position:** ```javascript // Original position const position = { size: -0.05, // Short position (negative size) price: 49500, productId: "BYBIT:FUTURE:BTCUSDT", // ... other fields }; // When user clicks X button, you receive: appCallback: (eventType, eventData) => { if (eventType === "PLACE_ORDER") { console.log("Exit order:", eventData); // { // side: "buy", // Opposite of short position // size: 0.05, // Absolute value of position size // orderType: "market", // Market order for immediate exit // productId: "BYBIT:FUTURE:BTCUSDT", // // ... other order fields // } } }; ``` **Handling Position Exit Orders:** ```javascript function handleTradingEvents(eventType, eventData) { switch (eventType) { case "PLACE_ORDER": const order = eventData; // Check if this is a position exit order // (You can identify it by matching the size with an existing position) const existingPosition = findPositionByProductId(order.productId); if (existingPosition && Math.abs(existingPosition.size) === order.size && isOppositeSide(existingPosition, order)) { // This is a position exit order console.log("Closing position:", existingPosition.id); // Execute the exit order await brokerAPI.placeOrder({ symbol: order.symbol, side: order.side, size: order.size, orderType: "market", productId: order.productId }); // After execution, remove the position from the chart const updatedPositions = currentPositions.filter( p => p.id !== existingPosition.id ); chart.setBrokerAccounts({ accountList: currentAccounts, orderBook: currentOrders, tradeBook: [...currentTrades, newTrade], positions: updatedPositions }); } else { // Regular order placement await brokerAPI.placeOrder(order); } break; case "MODIFY_POSITION": // ... handle position modifications ... break; } } // Helper function to check if order side is opposite to position function isOppositeSide(position, order) { if (position.size > 0) { // Long position, exit should be sell return order.side === "sell"; } else if (position.size < 0) { // Short position, exit should be buy return order.side === "buy"; } return false; } ``` **Important Notes:** 1. **Position Identification**: The exit order will have the same `productId` as the position being closed 2. **Size Matching**: The order size will exactly match the position size (absolute value) 3. **Market Orders**: Exit orders are typically market orders for immediate execution 4. **Full Exit**: The X button creates an order to fully close the position (not partial exit) 5. **Update Chart**: After executing the exit order, update the chart by removing the position from the `positions` array ## Complete Trading Implementation Example Here's a comprehensive example showing the complete trading implementation with order management, modification handling, and broker data integration: ### Complete Trading Chart Setup ```javascript // Complete trading implementation with all features class TradingChartManager { constructor() { this.chartInstance = null; this.currentOrderBook = []; this.currentTradeBook = []; this.currentPositions = []; this.currentAccountList = []; } async initialize() { // Create chart with trading enabled this.chartInstance = window.GoChartingSDK.createChart( "#chart-container", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1D", datafeed: this.createDatafeed(), licenseKey: "YOUR_LICENSE_KEY", trading: { enableTrading: true, }, appCallback: (eventType, message) => this.handleTradingEvent(eventType, message), onReady: (chartInstance) => { console.log(" Trading chart ready"); this.setupInitialBrokerData(); }, }, ); this.setupEventListeners(); } // Handle all trading events from the chart handleTradingEvent(eventType, message) { console.log(` Trading Event: ${eventType}`, message); switch (eventType) { case "PLACE_ORDER": this.addOrderToOrderBook(message); break; case "CANCEL_ORDER": this.removeOrderFromOrderBook(message); break; case "MODIFY_ORDER": this.modifyOrderInOrderBook(message); break; default: console.log("Unknown trading event:", eventType, message); } } // Add new order to orderbook addOrderToOrderBook(orderData) { const order = orderData.order || orderData; const security = orderData.security || {}; const ltp = orderData.ltp || this.getCurrentLTP(); const isMarketOrder = order.orderType === "market"; const executionPrice = isMarketOrder ? ltp : order.price; const newOrder = { orderId: `ORDER_${Date.now()}_${Math.random() .toString(36) .substr(2, 9)}`, datetime: new Date().toISOString(), timeStamp: Date.now(), status: isMarketOrder ? "filled" : "open", price: executionPrice, size: order.quantity || order.size, productId: order.symbol, remainingSize: isMarketOrder ? 0 : order.quantity || order.size, orderType: order.orderType, side: order.side, exchange: security.exchange || "BYBIT", symbol: security.symbol || "BTCUSDT", takeProfit: order.takeProfit || null, stopLoss: order.stopLoss || null, broker: "demo", productType: security.segment || "FUTURE", security: security, key: `demo-${order.symbol}-${Date.now()}`, validity: "DAY", isGC: true, }; this.currentOrderBook.push(newOrder); // For market orders, create position and trade if (isMarketOrder) { this.createPositionAndTrade(newOrder, ltp); } this.updateChartBrokerData(); console.log(" Order added to orderbook:", newOrder); } // Remove order from orderbook removeOrderFromOrderBook(orderData) { const orderId = orderData.orderId || orderData.order?.orderId; this.currentOrderBook = this.currentOrderBook.filter( (order) => order.orderId !== orderId, ); this.updateChartBrokerData(); console.log(" Order removed from orderbook:", orderId); } // Modify existing order modifyOrderInOrderBook(orderData) { const order = orderData.order || orderData; const orderId = order.orderId; const orderIndex = this.currentOrderBook.findIndex( (existingOrder) => existingOrder.orderId === orderId, ); if (orderIndex !== -1) { const existingOrder = this.currentOrderBook[orderIndex]; // Handle specific update types if (order.update) { switch (order.update) { case "DELETE_SL": existingOrder.stopLoss = null; console.log(" Deleted stop loss"); break; case "DELETE_TP": existingOrder.takeProfit = null; console.log(" Deleted take profit"); break; case "SHAPE_MODIFY": if (order.updateType === "TAKE_PROFIT") { existingOrder.takeProfit = order.takeProfit; console.log( " Updated take profit:", order.takeProfit, ); } else if (order.updateType === "STOP_LOSS") { existingOrder.stopLoss = order.stopLoss; console.log( " Updated stop loss:", order.stopLoss, ); } else if (order.updateType === "LIMIT_PRICE") { existingOrder.price = order.price; console.log(" Updated limit price:", order.price); } break; default: // Standard modifications if (order.price !== undefined) existingOrder.price = order.price; if (order.quantity !== undefined) { existingOrder.size = order.quantity; existingOrder.remainingSize = order.quantity; } break; } } existingOrder.modifiedAt = Date.now(); this.updateChartBrokerData(); console.log(" Order modified:", existingOrder); } } // Create position and trade for market orders createPositionAndTrade(order, executionPrice) { // Create trade record const trade = { tradeId: `TRADE_${Date.now()}_${Math.random() .toString(36) .substr(2, 9)}`, orderId: order.orderId, datetime: new Date().toISOString(), timeStamp: Date.now(), status: "filled", price: executionPrice, tradeSize: order.size, tradeValue: executionPrice * order.size, productId: order.productId, orderType: order.orderType, side: order.side, broker: "demo", productType: order.productType, security: order.security, fee: { currency: "USDT", cost: 0, rate: 0 }, key: `demo-${order.productId}-${Date.now()}`, isGC: true, }; this.currentTradeBook.push(trade); // Update or create position this.updatePosition(order, executionPrice); } // Update position based on trade updatePosition(order, executionPrice) { const existingPositionIndex = this.currentPositions.findIndex( (pos) => pos.productId === order.productId, ); if (existingPositionIndex !== -1) { // Update existing position const position = this.currentPositions[existingPositionIndex]; const currentSize = position.size || 0; const newSize = order.side === "buy" ? currentSize + order.size : currentSize - order.size; if (newSize === 0) { // Position closed this.currentPositions.splice(existingPositionIndex, 1); } else { // Update position position.size = newSize; position.price = executionPrice; // Simplified - should calculate average position.amount = newSize * executionPrice; } } else if (order.size > 0) { // Create new position const newPosition = { size: order.side === "buy" ? order.size : -order.size, size_currency: order.size, price: executionPrice, amount: order.size * executionPrice, productId: order.productId, broker: "demo", productType: order.productType, currency: "USDT", id: `POS_${Date.now()}`, underlying: order.productId, unPnl: 0, rPnl: 0, pnl: 0, security: order.security, symbol: order.symbol, segment: order.productType, exchange: order.exchange, key: `demo-${order.productId}-${Date.now()}`, isGC: true, }; this.currentPositions.push(newPosition); } } // Reset all broker data resetBroker() { this.currentOrderBook = []; this.currentTradeBook = []; this.currentPositions = []; this.updateChartBrokerData(); console.log(" Broker data reset - All data cleared"); } // Update chart with current broker data updateChartBrokerData() { if (!this.chartInstance) return; const brokerData = { accountList: this.currentAccountList, orderBook: this.currentOrderBook, tradeBook: this.currentTradeBook, positions: this.currentPositions, }; this.chartInstance.setBrokerAccounts(brokerData); } // Get current LTP (simulated) getCurrentLTP() { return 115234.5 + (Math.random() - 0.5) * 1000; // Simulated price } // Setup initial demo data setupInitialBrokerData() { this.currentAccountList = [ { account_id: "DEMO_001", AccountID: "DEMO_001", AccountType: "Demo Trading", label: "Demo Account", currency: "USD", balance: 100000, equity: 100000, margin: 0, freeMargin: 100000, }, ]; this.updateChartBrokerData(); } // Setup UI event listeners setupEventListeners() { // Buy button document.getElementById("buy-btn")?.addEventListener("click", () => { this.placeBuyOrder(); }); // Sell button document.getElementById("sell-btn")?.addEventListener("click", () => { this.placeSellOrder(); }); // Reset broker button document .getElementById("reset-broker-btn") ?.addEventListener("click", () => { this.resetBroker(); }); // Order type change document .getElementById("order-type") ?.addEventListener("change", (e) => { this.handleOrderTypeChange(e.target.value); }); } // Place buy order from form placeBuyOrder() { const orderData = this.getOrderDataFromForm(); orderData.side = "buy"; this.handleTradingEvent("PLACE_ORDER", { order: orderData, security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", }, ltp: this.getCurrentLTP(), }); } // Place sell order from form placeSellOrder() { const orderData = this.getOrderDataFromForm(); orderData.side = "sell"; this.handleTradingEvent("PLACE_ORDER", { order: orderData, security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", }, ltp: this.getCurrentLTP(), }); } // Get order data from form inputs getOrderDataFromForm() { return { symbol: "BYBIT:FUTURE:BTCUSDT", quantity: parseInt( document.getElementById("quantity")?.value || "100", ), orderType: document.getElementById("order-type")?.value || "market", price: parseFloat( document.getElementById("limit-price")?.value || "0", ), stopLoss: parseFloat(document.getElementById("stop-loss")?.value) || null, takeProfit: parseFloat(document.getElementById("take-profit")?.value) || null, }; } // Handle order type changes handleOrderTypeChange(orderType) { const limitPriceInput = document.getElementById("limit-price"); if (limitPriceInput) { limitPriceInput.disabled = orderType === "market"; if (orderType === "market") { limitPriceInput.value = ""; } } } // Create simple datafeed for demo createDatafeed() { return { getBars: async (symbolInfo, resolution, periodParams) => { // Return demo OHLCV data return { bars: [], meta: { noData: false } }; }, resolveSymbol: (symbolName, onResolve, onError) => { const parts = String(symbolName).split(":"); const exchange = parts[0] || "BYBIT"; const segment = parts.length >= 3 ? parts[1] : "FUTURE"; const symbol = parts[parts.length - 1] || "BTCUSDT"; const validIntervals = ["1", "5", "15", "30", "60", "1D"]; onResolve({ exchange, segment, symbol, name: "BTC / USDT PERPETUAL FUTURES", asset_type: "CRYPTO", source_id: symbol, pair: { from: "BTC", to: "USDT" }, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.1, display_tick_size: 1, volume_size_increment: 0.001, max_tick_precision: 1, max_volume_precision: 3, quote_currency: "USDT", future_type: "PERP", supports: { footprint: true }, exchange_info: { name: exchange.toLowerCase(), code: exchange, zone: "UTC", hours: Array.from({ length: 7 }, () => ({ open: true })), valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: "BTC / USDT PERPETUAL FUTURES", type: "crypto", session: "24x7", timezone: "UTC", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); }, searchSymbols: (userInput, callback) => { callback([]); }, }; } } // Initialize trading chart const tradingManager = new TradingChartManager(); tradingManager.initialize(); ``` ## Usage Example Here's a comprehensive example showing how to integrate all broker data structures: ```javascript // Create chart with trading enabled const chart = window.GoChartingSDK.createChart("#chart-container", { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", trading: { enableTrading: true, }, appCallback: handleTradingEvents, onReady: (chartInstance) => { // Initialize with broker data setupBrokerData(chartInstance); }, }); // Handle trading events from the chart function handleTradingEvents(eventType, eventData) { switch (eventType) { case "CREATE_ORDER": console.log("Order creation requested:", eventData); break; case "PLACE_ORDER": console.log("Order placed:", eventData); addOrderToSystem(eventData); break; case "CANCEL_ORDER": console.log("Order cancelled:", eventData); removeOrderFromSystem(eventData.orderId); break; case "MODIFY_ORDER": console.log("Order modified:", eventData); updateOrderInSystem(eventData); break; } } // Set up initial broker data function setupBrokerData(chartInstance) { const brokerData = { accountList: [ { account_id: "LIVE_001", AccountID: "LIVE_001", AccountType: "Live Trading", label: "Live Trading Account", currency: "USD", balance: 50000, equity: 52500, margin: 2500, freeMargin: 47500, }, ], orderBook: [ { orderId: "ORD_001", datetime: new Date(), timeStamp: new Date().getTime(), status: "open", price: 45000, size: 0.1, productId: "BYBIT:FUTURE:BTCUSDT", remainingSize: 0.1, orderType: "limit", side: "buy", exchange: "BYBIT", symbol: "BTCUSDT", broker: "live", productType: "FUTURE", security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, fee: { currency: "USDT", cost: 0.0, rate: 0.001 }, key: "live-BYBIT:FUTURE:BTCUSDT-ORD_001", validity: "GTC", commissions: 0, isGC: true, }, ], tradeBook: [ { tradeId: "TRD_001", orderId: "ORD_FILLED_001", datetime: new Date(Date.now() - 7200000), timeStamp: new Date(Date.now() - 7200000).getTime(), status: "filled", price: 46000, tradeSize: 0.05, tradeValue: 2300, productId: "BYBIT:FUTURE:BTCUSDT", orderType: "limit", side: "buy", broker: "live", productType: "FUTURE", security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, fee: { currency: "USDT", cost: 2.3, rate: 0.001 }, key: "live-BYBIT:FUTURE:BTCUSDT-TRD_001", type: "tradeExecuted", isGC: true, }, ], positions: [ { size: 0.05, size_currency: 0.05, price: 46000, amount: 2300, productId: "BYBIT:FUTURE:BTCUSDT", broker: "live", productType: "FUTURE", currency: "USDT", id: "POS_001", underlying: "BYBIT:FUTURE:BTCUSDT", unPnl: 150, // Current unrealized P&L rPnl: 0, // Realized P&L pnl: 150, // Total P&L security: { symbol: "BTCUSDT", exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, symbol: "BTCUSDT", segment: "FUTURE", exchange: "BYBIT", key: "live-BYBIT:FUTURE:BTCUSDT-POS_001", isGC: true, }, ], }; // Set the broker data on the chart chartInstance.setBrokerAccounts(brokerData); console.log(" Broker data loaded successfully"); } // Add new order to the system and update chart function addOrderToSystem(orderData) { // Create order object in the required format const newOrder = { orderId: generateOrderId(), datetime: new Date(), timeStamp: new Date().getTime(), status: "open", price: orderData.price || 0, size: orderData.quantity || 0, productId: orderData.symbol, remainingSize: orderData.quantity || 0, orderType: orderData.orderType || "limit", side: orderData.side, exchange: "BYBIT", symbol: orderData.symbol.replace("BYBIT:FUTURE:", ""), broker: "live", productType: "FUTURE", security: { symbol: orderData.symbol.replace("BYBIT:FUTURE:", ""), exchange: "BYBIT", segment: "FUTURE", tick_size: 0.01, lot_size: 0.001, }, fee: { currency: "USDT", cost: 0.0, rate: 0.001 }, key: `live-${orderData.symbol}-${generateOrderId()}`, validity: "GTC", commissions: 0, isGC: true, }; // Add to your order management system // ... your order management logic ... // Update chart with new broker data updateChartBrokerData(); } // Update chart with current broker data function updateChartBrokerData() { const currentBrokerData = { accountList: getCurrentAccounts(), orderBook: getCurrentOrders(), tradeBook: getCurrentTrades(), positions: getCurrentPositions(), }; chart.setBrokerAccounts(currentBrokerData); } // Utility function to generate unique order IDs function generateOrderId() { return `ORD_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } ``` ## Best Practices ### 1. **Real-time Updates** Always call `setBrokerAccounts()` whenever your broker data changes: ```javascript // When order status changes function onOrderStatusChange(orderId, newStatus) { updateOrderStatus(orderId, newStatus); updateChartBrokerData(); // Update chart immediately } // When new trade is executed function onTradeExecuted(tradeData) { addTradeToHistory(tradeData); updatePositions(tradeData); updateChartBrokerData(); // Update chart immediately } ``` ### 2. **Data Validation** Validate your data before sending to the chart: ```javascript function validateBrokerData(data) { // Validate required fields if (!data.accountList || !Array.isArray(data.accountList)) { throw new Error("accountList is required and must be an array"); } // Validate order structure data.orderBook.forEach((order) => { if (!order.orderId || !order.symbol || !order.side) { throw new Error("Invalid order structure"); } }); return true; } ``` ### 3. **Performance Optimization** For large datasets, consider pagination or filtering: ```javascript function updateChartBrokerData() { const brokerData = { accountList: getCurrentAccounts(), orderBook: getCurrentOrders().slice(0, 100), // Limit to 100 recent orders tradeBook: getCurrentTrades().slice(0, 50), // Limit to 50 recent trades positions: getCurrentPositions(), // All positions (usually small) }; chart.setBrokerAccounts(brokerData); } ``` ### 4. **Error Handling** Always wrap `setBrokerAccounts()` calls in try-catch: ```javascript function updateChartBrokerData() { try { const brokerData = getBrokerData(); validateBrokerData(brokerData); chart.setBrokerAccounts(brokerData); console.log(" Chart updated successfully"); } catch (error) { console.error(" Failed to update chart:", error); // Handle error appropriately } } ``` ## Quick Reference ### Order Modification Event Types | Update Type | UpdateType | Action | Example | | -------------- | ------------- | ------------------ | --------------------------------------------------------------------------- | | `DELETE_SL` | - | Remove stop loss | `{ update: "DELETE_SL" }` | | `DELETE_TP` | - | Remove take profit | `{ update: "DELETE_TP" }` | | `SHAPE_MODIFY` | `TAKE_PROFIT` | Update TP price | `{ update: "SHAPE_MODIFY", updateType: "TAKE_PROFIT", takeProfit: 125000 }` | | `SHAPE_MODIFY` | `STOP_LOSS` | Update SL price | `{ update: "SHAPE_MODIFY", updateType: "STOP_LOSS", stopLoss: 110000 }` | | `SHAPE_MODIFY` | `LIMIT_PRICE` | Update limit price | `{ update: "SHAPE_MODIFY", updateType: "LIMIT_PRICE", price: 115500 }` | ### Trading Panel Components | Component | Purpose | Event | | --------------- | ---------------- | -------------------------------------- | | BUY Button | Place buy order | `PLACE_ORDER` with `side: "buy"` | | SELL Button | Place sell order | `PLACE_ORDER` with `side: "sell"` | | Reset Broker | Clear all data | Resets orderBook, tradeBook, positions | ### Order Status Flow ``` Market Order: Form → PLACE_ORDER → "filled" → Position + Trade Limit Order: Form → PLACE_ORDER → "open" → (waits for execution) ``` ### Required Data Structures ```javascript // Minimum required for setBrokerAccounts() { accountList: [{ account_id, currency, balance }], orderBook: [{ orderId, symbol, side, orderType, status, price, size }], tradeBook: [{ tradeId, orderId, price, tradeSize, side, status }], positions: [{ size, price, productId, symbol, pnl }] } ``` ## Related Documentation - [Trading Integration Tutorial](../tutorials/trading-integration.md) - [Chart API Reference](./chart.md) - [Event Handling](./events.md) - [Live Examples](https://codepen.io/Admin-GoCharting/pen/xbwvBbe) - Advanced Trading Demo File: docs/api/types.md ================================== # TypeScript Definitions Complete TypeScript type definitions for the GoCharting SDK. ## Enums & literal unions Catalog of exported unions: **[Enums & literal unions](./enums.md)**. ### Chart / widget - [ChartActionId](./enums.md#chartactionid) - [Chart type strings](./enums.md#chart-type-strings) - [VisibilityType](./enums.md#visibilitytype) - [ChartTheme](./enums.md#charttheme) - [ChartFeatureId](./enums.md#chartfeatureid) - [MarketStatusInfo](./enums.md#marketstatusinfo) - [TimeFrameInput](./enums.md#timeframeinput) ### Trading - [OrderType](./enums.md#ordertype--orderside--orderstatus--timeinforce) · [OrderSide](./enums.md#ordertype--orderside--orderstatus--timeinforce) · [OrderStatus](./enums.md#ordertype--orderside--orderstatus--timeinforce) · [TimeInForce](./enums.md#ordertype--orderside--orderstatus--timeinforce) - [ExecutionDirection](./enums.md#executiondirection) ### Events - [TradingEventType](./enums.md#tradingeventtype) - [AppCallbackEventType](./enums.md#appcallbackeventtype) ## Core Types ### Chart Types ```typescript // Chart instance returned by createChart() interface ChartInstance { // Lifecycle destroy(): void; isDestroyed(): boolean; getChartInstance(): ChartWidgetInstance; // Widget façade activeChart(): IChartApi; chart(index: number): IChartApi; chartsCount(): number; activeChartIndex(): number; setActiveChart(index: number): void; layout(): string; setLayout(layout: string): void; setLayoutSizes(sizes: LayoutSizes, disableUndo?: boolean): void; resetLayoutSizes(disableUndo?: boolean): void; unloadUnusedCharts(): void; applyOverrides(overrides: ApplyOverridesInput): void; applyStudiesOverrides(overrides: StudiesOverridesMap): void; applyTradingCustomization(overrides: TradingCustomization): void; setCSSCustomProperty(name: string, value: string): void; getCSSCustomPropertyValue(name: string): string; addCustomCSSFile(url: string): void; customThemes(): Promise; features(): IFeaturesApi; setFeatureEnabled(id: ChartFeatureId | string, enabled: boolean): void; getFeatureEnabled(id: ChartFeatureId | string): boolean; getFeatures(): FeatureFlags; headerReady(): Promise; createButton(options?: CreateButtonOptions): HTMLButtonElement; createDropdown(options: CreateDropdownOptions): Promise; removeButton(button: HTMLElement | string): void; onShortcut(shortcut: ShortcutInput, callback: () => void): () => void; onContextMenu(callback: ContextMenuCallback): void; closePopupsAndDialogs(): void; showConfirmDialog( titleOrParams: string | ConfirmDialogParams, content?: string | string[] | ((confirmed: boolean) => void), callbackOrOk?: ((confirmed: boolean) => void) | string, optionsOrCancel?: { okText?: string; cancelText?: string } | string, ): Promise; showNoticeDialog( titleOrParams: string | NoticeDialogParams, content?: string | string[], callback?: () => void, ): Promise; selectLineTool(linetool: string): void; selectedLineTool(): string; hideAllDrawingTools(): ISyncApi; lockAllDrawingTools(): ISyncApi; magnetEnabled(): ISyncApi; magnetMode(): INumberSyncApi; takeClientScreenshot(options?: ClientSnapshotOptions): Promise; takeScreenshot(): Promise; clearUndoHistory(): void; undoRedoState(): UndoRedoState; resetCache(): void; getStudiesList(): string[]; getStudyInputs(studyName: string): StudyInputInformation[]; getStudyStyles(studyName: string): StudyStyleInfo; supportedChartTypes(): string[]; getIntervals(): string[]; symbolInterval(): SymbolIntervalResult | null; mainSeriesPriceFormatter(): PriceFormatter; currencyAndUnitVisibility(): IVisibilityApi; customSymbolStatus(): ICustomSymbolStatusApi; navigationButtonsVisibility(): IVisibilityWatchedValue; paneButtonsVisibility(): IVisibilityWatchedValue; dateFormat(): IStringWatchedValue; timeHoursFormat(): IStringWatchedValue; symbolSync(): ISyncApi; intervalSync(): ISyncApi; drawOnAllChartsEnabled(): ISyncApi; crosshairSync(): ISyncApi; dateRangeSync(): ISyncApi; timeSync(): ISyncApi; subscribe(event: WidgetSubscribeEvent, callback: WidgetSubscribeCallback): void; unsubscribe(event: WidgetSubscribeEvent, callback: WidgetSubscribeCallback): void; getAllCharts(): MultiChartInfo; // Chart control / trading setSymbol(newSymbol: string): void; setInterval(newInterval: string): void; resize(width: number | string, height: number | string): void; setBrokerAccounts(data: BrokerAccountData): void; } // Chart theme type type ChartTheme = "light" | "dark"; // Chart initialization configuration (createChart function) interface ChartConfig { // REQUIRED - Core configuration datafeed: Datafeed; // Your datafeed implementation symbol: string; // Trading symbol (e.g., "AAPL", "BYBIT:FUTURE:BTCUSDT") interval: string; // Chart interval (e.g., "1m", "5m", "1D") licenseKey: string; // Your SDK license key // OPTIONAL - Display options theme?: ChartTheme; // Chart theme (default: "light") autosize?: boolean; // Enable automatic resizing (default: true) width?: number | string; // Chart width (default: "100%") height?: number | string; // Chart height (default: "100%") // OPTIONAL - Feature flags skipLicenseValidation?: boolean; // Skip license validation - demo only (default: false) debugLog?: boolean; // Enable debug logging (default: false) disableSearch?: boolean; // Hide search bar in top bar (default: false) disableCompare?: boolean; // Hide compare button in top bar (default: false) disabled_features?: string[]; // Construct-time featureset (P4-1) enabled_features?: string[]; // Construct-time featureset (P4-1) touchMode?: boolean; // Force full JS mobile Chart-tab shell (default: false) isNativeApp?: boolean; // Native WebView: hide JS bars; emit OPEN_CONTEXT_MENU (default: false) // OPTIONAL - Trading configuration trading?: TradingConfig; // Trading panel and features configuration // OPTIONAL - UI customization exclude?: ExcludeOptions; // Exclude specific UI elements // OPTIONAL - Event callbacks appCallback?: ( eventType: AppCallbackEventType, message: AppCallbackMessage, onClose?: () => void, ) => void; // Trading and app events onReady?: (chartInstance: ChartInstance) => void; // Chart ready callback onError?: (error: Error | ChartError | string) => void; // Error callback } // UI element exclusion options interface ExcludeOptions { leftPanel?: boolean; // Hide left panel rightPanel?: boolean; // Hide right panel topBar?: boolean; // Hide top bar bottomBar?: boolean; // Hide bottom bar chartPanel?: boolean; // Hide chart panel drawingToolbar?: boolean; // Hide drawing toolbar timeframeSelector?: boolean; // Hide timeframe selector symbolSearch?: boolean; // Hide symbol search compareSymbols?: boolean; // Hide compare symbols indicators?: boolean; // Hide indicators settings?: boolean; // Hide settings fullscreen?: boolean; // Hide fullscreen button screenshot?: boolean; // Hide screenshot button alerts?: boolean; // Hide alerts watchlist?: boolean; // Hide watchlist news?: boolean; // Hide news calendar?: boolean; // Hide calendar } // Trading configuration options interface TradingConfig { // Core Trading Settings enableTrading?: boolean; // Enable trading features (default: false) // UI Display Options showReverseButton?: boolean; // Show reverse button in trading panel (default: true) showOpenOrders?: boolean; // Show open orders panel (default: true) showPositions?: boolean; // Show positions panel (default: true) showExecutions?: boolean; // Show executions/trades panel (default: true) showNotifications?: boolean; // Show trading notifications (default: true) // Trading Behavior beep?: boolean; // Play sound on order execution (default: true) quickTradeMode?: boolean; // Enable quick trade mode (default: false) // Take Profit Settings enableTakeProfitDefaults?: boolean; // Enable take profit defaults (default: false) defaultTakeProfitSpread?: number; // Default take profit spread (default: 1) defaultTakeProfitSpreadType?: "tick" | "price" | "percent"; // Default: "tick" // Stop Loss Settings enableStopLossDefaults?: boolean; // Enable stop loss defaults (default: false) defaultStopLossSpread?: number; // Default stop loss spread (default: 1) defaultStopLossSpreadType?: "tick" | "price" | "percent"; // Default: "tick" // Position Display Settings boxAlignment?: "left" | "right"; // Trading box alignment (default: "right") lineCategory?: "extended" | "compact"; // Order/position line category (default: "extended") } ``` ### Widget / IChartApi types (Phase 0–1E) Types for `activeChart()` / `chart(index)` and related façade payloads. Fields use GoCharting shapes. See the [Chart API — Widget façade](./chart.md#widget-façade). ```typescript type WidgetSubscribeEvent = | "activeChartChanged" | "layout" | "onTick" | string; type WidgetSubscribeCallback = (payload: any) => void; /** Cross-chart sync controller — `symbolSync()` / `intervalSync()` / `drawOnAllChartsEnabled()` / `crosshairSync()` / `dateRangeSync()` / `timeSync()` */ type ISyncApi = { value(): boolean; setValue(value: boolean): void; subscribe(callback: (value: boolean) => void): () => void; }; /** Multi-chart pane sizes as percentages (P2-2) */ type LayoutSizes = { rows?: number[]; columns?: number[]; }; /** Chart appearance override (P3-1) — bare appearance or `{ appearance }` */ type ApplyOverridesInput = | Record | { appearance: Record; [key: string]: any }; /** Studies overrides keyed by indicator type (P3-1) */ type StudiesOverridesMap = Record< string, | Record | { appearance?: Record; options?: Record } >; /** Root `trading` prefs merge (P3-1) */ type TradingCustomization = Record; /** CSS custom property token map (P3-2) */ type CustomThemeTokens = Record; type CustomThemesInput = | CustomThemeTokens | { light?: CustomThemeTokens; dark?: CustomThemeTokens }; type ICustomThemesApi = { applyCustomThemes(themes: CustomThemesInput): void; resetCustomThemes(): void; getCustomThemes(): CustomThemesInput | null; }; /** Runtime chrome feature ids (P3-3) */ type ChartFeatureId = | "topBar" | "bottomBar" | "drawingToolbar" | "leftPanel" | "rightPanel" | "symbolSearch" | "compare"; type FeatureFlags = Record; type IFeaturesApi = { value(): FeatureFlags; setValue(partial: Partial): void; subscribe(callback: (value: FeatureFlags) => void): () => void; }; /** Header custom controls (P3-4) */ type HeaderAlign = "left" | "right"; type CreateButtonOptions = { align?: HeaderAlign; useTradingViewStyle?: boolean; text?: string; title?: string; onClick?: () => void; }; type DropdownItem = { title: string; onSelect: () => void }; type CreateDropdownOptions = { title: string; tooltip?: string; icon?: string; items: DropdownItem[]; align?: HeaderAlign; }; type IDropdownApi = { applyOptions(options: Partial): void; remove(): void; }; /** Keyboard / context menu (P3-5) */ type ShortcutInput = string | Array; type ContextMenuItemPosition = "top" | "bottom"; type ContextMenuItem = { position?: ContextMenuItemPosition; /** `"-"` separator; `"-Label"` hide matching built-in */ text: string; click?: () => void; }; type ContextMenuCallback = ( unixtime: number, price: number, ) => ContextMenuItem[] | void; /** Dialogs (P3-6) */ type NoticeDialogParams = { title: string; body?: string; callback?: () => void; }; type ConfirmDialogParams = { title: string; body?: string; callback?: (confirmed: boolean) => void; okText?: string; cancelText?: string; }; /** Numeric watched value (P3-7 magnetMode: 0 off / 1 on) */ type INumberSyncApi = { value(): number; setValue(value: number): void; subscribe(callback: (value: number) => void): () => void; }; /** Options for takeClientScreenshot (P3-8 / TV ClientSnapshotOptions subset) */ type ClientSnapshotOptions = { backgroundColor?: string; includeCrosshair?: boolean; hideStudiesFromLegend?: boolean; }; /** Undo/redo snapshot (P3-9 / TV UndoRedoState) */ type UndoRedoState = { enableUndo: boolean; enableRedo: boolean; undoText: string; redoText: string; originalUndoText: string; originalRedoText: string; }; /** Study input descriptor (P3-10 / TV StudyInputInformation) */ type StudyInputInformation = { id: string; name: string; type: string; localizedName: string; }; /** Study style metadata (P3-10 / TV StudyStyleInfo subset) */ type StudyStyleInfo = { plots?: { id: string; type: string }[]; styles?: Record; defaults?: { styles?: Record }; }; /** Active symbol + interval (P3-11 / TV SymbolIntervalResult) */ type SymbolIntervalResult = { symbol: string; interval: string; }; /** Number formatter (P3-12 / TV INumberFormatter) */ type PriceFormatter = { format: (price: number) => string; }; /** Price-scale currency/unit visibility (P3-12) */ type CurrencyUnitVisibility = | "alwaysOn" | "alwaysOff" | "visibleOnMouseOver"; type IVisibilityApi = { value(): CurrencyUnitVisibility; setValue(value: CurrencyUnitVisibility): void; subscribe(callback: (value: CurrencyUnitVisibility) => void): () => void; }; type ICustomSymbolStatusApi = { symbol(symbolId: string): ICustomSymbolStatusAdapter; hideAll(): void; getAll(): Record; }; /** Watched visibility (P3-13 nav/pane buttons) */ type IVisibilityWatchedValue = { value(): CurrencyUnitVisibility; setValue(value: CurrencyUnitVisibility): void; subscribe(callback: (value: CurrencyUnitVisibility) => void): () => void; }; /** Watched string (P3-13 dateFormat / timeHoursFormat) */ type IStringWatchedValue = { value(): string; setValue(value: string): void; subscribe(callback: (value: string) => void): () => void; }; type ChartSelectedMessage = { id: string; chartId: string; idx: number; symbol: string; interval: string; }; type MultiChartInfo = { isMultichartingEnabled: boolean; /** GoCharting layout id when emitted from CHART_MODE_CHANGED / setLayout */ layout?: string; charts: ChartSelectedMessage[]; }; /** Visible time range in Unix seconds. */ type VisibleTimeRange = { from: number; to: number }; /** Visible bar indices in loaded data. */ type VisibleBarsRange = { from: number; to: number }; type MarketStatusInfo = { isOpen?: boolean; statusText?: string; dataStatus?: string; delaySeconds?: number; }; /** Extended symbol info — prefer max_tick_precision / tick_size (not TV pricescale / minmov). */ type SymbolExtInfo = { symbol: string; full_name?: string; name?: string; exchange?: string; segment?: string; tick_size?: number; display_tick_size?: number; max_tick_precision?: number; max_volume_precision?: number; data_status?: string; delay_seconds?: number; supported_resolutions?: string[]; [key: string]: unknown; }; /** Pane from config.charts / chartOrder — GC id/idx (not TV paneIndex). */ type ChartPaneInfo = { idx: number; id: string; heightFraction: number; label?: string; type?: string; visible?: boolean; }; type ExportedSeriesData = { schema: Array<{ name: string; type: string }>; data: Array>; }; type TimeFrameInput = | string | { from: number; to: number } | { type: "period-back"; value: string } | { type: "time-range"; from: number; to: number }; /** Create/input payload for drawings — GoCharting fields only. */ type CreateShapeInput = { type?: string; name?: string; shape?: Record; options?: DrawingOptions; appearance?: DrawingAppearance; chartId?: string; visible?: boolean; [key: string]: unknown; }; type ShapeInfo = { id: string; chartId: string; kind: string; type?: string; name?: string; shape?: Record; options?: DrawingOptions; appearance?: DrawingAppearance; visible?: boolean; zIndex?: number; groupId?: string | null; [key: string]: unknown; }; type ObjectGroupInfo = { id: string; objectIds: string[]; zIndex?: number; visible?: boolean; lock?: boolean; label?: string; [key: string]: unknown; }; type LineToolsPaneState = { objects: Record; objectGroups?: Record; }; type LineToolsState = { charts: Record; }; type ZOrderOperations = { bringToFront: boolean; bringForward: boolean; sendToBack: boolean; sendBackward: boolean; }; type ShapeSelection = { chartId: string; objectId: string; selectedPart?: unknown; } | null; type ShapesGroupController = { createGroup(objectIds: string[], chartId?: string): string; ungroup(groupId: string, chartId?: string): void; getGroups(chartId?: string): ObjectGroupInfo[]; }; /** Per-chart API from widget.activeChart() / widget.chart(index). */ type IChartApi = { // Phase 0 chartIndex(): number; symbol(): string | null; symbolExt(): SymbolExtInfo | null; interval(): string | null; resolution(): string | null; chartType(): string | null; symbolInterval(): { symbol: string; interval: string } | null; setSymbol(symbol: string): void; setInterval(interval: string): void; setResolution(interval: string): void; setChartType(chartType: string): void; addIndicator(indicator: { type?: string; id?: string }): void; addDrawing(drawing: CreateShapeInput, chartId?: string): void; deleteObject(objectId: string, chartId?: string): void; updateSettings(settings: Partial): void; getChartState(): ChartState | null; setChartState(newState: ChartState): void; // Phase 1A getVisibleRange(): VisibleTimeRange | null; setVisibleRange(range: VisibleTimeRange): void; getVisibleBarsRange(): VisibleBarsRange | null; goToDate(date: string | Date | { date1: any; date2: any }): void; setTimeFrame(timeframe: TimeFrameInput): void; zoomOut(): void; canZoomOut(): boolean; resetData(): void; // viewport/scale reset — not a datafeed refetch exportData(options?: { includeVolume?: boolean; useFullData?: boolean; }): ExportedSeriesData | null; getPanes(): ChartPaneInfo[]; getAllPanesHeight(): number[]; getTimeScale(): { getVisibleRange: () => VisibleTimeRange | null; barSpacing: () => number | null; }; getSeries(): { id: string; type: string; title?: string } | null; priceFormatter(): { format: (price: number) => string }; maximizeChart(): void; isMaximized(): boolean; marketStatus(): MarketStatusInfo | null; inactivityGaps(): boolean; // Phase 1B createShape(drawing: CreateShapeInput): string; createMultipointShape(drawing: CreateShapeInput): string; createAnchoredShape(drawing: CreateShapeInput): string; getAllShapes(chartId?: string): ShapeInfo[]; getShapeById(objectId: string, chartId?: string): ShapeInfo | null; removeEntity(objectId: string, chartId?: string): void; removeAllShapes(chartId?: string): void; getLineToolsState(): LineToolsState; applyLineToolsState(state: LineToolsState): void; reloadLineToolsFromServer(): void; // stub until Phase 5 — throws bringToFront(objectId: string, chartId?: string): void; bringForward(objectId: string, chartId?: string): void; sendToBack(objectId: string, chartId?: string): void; sendBackward(objectId: string, chartId?: string): void; availableZOrderOperations(objectId: string, chartId?: string): ZOrderOperations; selection(): ShapeSelection; showPropertiesDialog(objectId?: string, chartId?: string): void; shapesGroupController(): ShapesGroupController; // Phase 1C — studies getAllStudies(chartId?: string): StudyInfo[]; getStudyById(objectId: string, chartId?: string): StudyInfo | null; removeAllStudies(): void; createStudyTemplate(nameOrOptions?: string | { name?: string; save?: boolean }): StudyTemplate; applyStudyTemplate(template: StudyTemplate | { indicators?: StudyTemplateItem[] }): void; loadChartTemplate( template: string | StudyTemplate | ChartState | Record, ): void; // Phase 1D — library-native trading lines (not Broker API) createOrderLine(): IOrderLineApi; createPositionLine(): IPositionLineApi; createExecutionShape(): IExecutionShapeApi; // Phase 1E — chart events + actions onSymbolChanged(): ISubscription; onIntervalChanged(): ISubscription; onChartTypeChanged(): ISubscription; onDataLoaded(): ISubscription; onVisibleRangeChanged(): ISubscription<{ from: number; to: number }>; crossHairMoved(): ISubscription; onHoveredSourceChanged(): ISubscription; dataReady(callback: () => void): void; executeActionById(actionId: ChartActionId | string): void; requestSelectBar(): Promise; cancelSelectBar(): void; }; type ISubscription = { subscribe(callback: (value: T) => void): () => void; unsubscribe(callback?: (value: T) => void): void; }; type CrossHairMovedEvent = { time: number | null; price: number | null; }; type HoveredSourceEvent = { entityId: string | null; chartId?: string; type?: string; } | null; type SelectBarResult = { index: number; time: number; }; type ChartActionId = | "chartReset" | "zoomIn" | "zoomOut" | "undo" | "redo" | "invertScale" | "logScale" | "magnet"; type ExecutionDirection = "buy" | "sell"; /** Fluent order-line stub — see Chart API Phase 1D */ type IOrderLineApi = { remove(): void; getPrice(): number; setPrice(price: number): IOrderLineApi; getText(): string; setText(text: string): IOrderLineApi; getQuantity(): string; setQuantity(value: string | number): IOrderLineApi; getTooltip(): string; setTooltip(text: string): IOrderLineApi; onModify(callback: () => void): IOrderLineApi; onMove(callback: () => void): IOrderLineApi; onCancel(callback: () => void): IOrderLineApi; }; /** Fluent position-line stub — see Chart API Phase 1D */ type IPositionLineApi = { remove(): void; getPrice(): number; setPrice(price: number): IPositionLineApi; getText(): string; setText(text: string): IPositionLineApi; getQuantity(): string; setQuantity(value: string | number): IPositionLineApi; getTooltip(): string; setTooltip(text: string): IPositionLineApi; onClose(callback: () => void): IPositionLineApi; onReverse(callback: () => void): IPositionLineApi; onModify(callback: () => void): IPositionLineApi; }; /** Fluent execution-shape stub — see Chart API Phase 1D */ type IExecutionShapeApi = { remove(): void; getPrice(): number; setPrice(price: number): IExecutionShapeApi; getTime(): number; setTime(time: number): IExecutionShapeApi; getDirection(): ExecutionDirection; setDirection(direction: ExecutionDirection): IExecutionShapeApi; getText(): string; setText(text: string): IExecutionShapeApi; getTooltip(): string; setTooltip(text: string): IExecutionShapeApi; }; type StudyInfo = { id: string; chartId: string; kind: string; type?: string; name?: string; options?: Record; appearance?: Record; visible?: boolean; zIndex?: number; }; type StudyTemplateItem = { type: string; name?: string; options?: Record; appearance?: Record; }; type StudyTemplate = { id?: string | number; name?: string; indicators: StudyTemplateItem[]; createdAt?: string; }; ``` ### Datafeed Types ```typescript // IMPLEMENTED - Datafeed interface (simple object, not class inheritance) interface Datafeed { // IMPLEMENTED - Required methods getBars( symbolInfo: SymbolInfo, resolution: string, periodParams: PeriodParams, ): Promise; resolveSymbol( symbolName: string, onResolve: (symbolInfo: SymbolInfo) => void, onError: (error: string) => void, ): void; // OPTIONAL - Symbol search method searchSymbols?( userInput: string, callback: (symbols: SearchResult[]) => void, ): void; // OPTIONAL - Cleanup method destroy?(): void; } // Period parameters for data requests interface PeriodParams { from: number; // Unix timestamp to: number; // Unix timestamp firstDataRequest: boolean; countBack?: number; } // Bar data result interface BarsResult { bars: Bar[]; meta?: { noData?: boolean; nextTime?: number; }; } ``` ### Symbol and Market Data Types ### Symbol Naming Convention **Important**: Symbol names must **NOT** contain special characters such as `/`, `\`, `:`, `*`, `?`, `"`, `<`, `>`, or `|`. For example: - `EUR/USD` - **Invalid** (contains `/`) - `EURUSD` - **Valid** - `BTC/USDT` - **Invalid** (contains `/`) - `BTCUSDT` - **Valid** The `symbol` field should contain only alphanumeric characters. Use the `name` or `description` field for human-readable display names that may include special characters. ```typescript // Symbol information (complete API response format) interface SymbolInfo { // Basic Symbol Information exchange: string; // Exchange name (e.g., "BYBIT") segment: string; // Market segment (e.g., "FUTURE", "SPOT", "OPTION") symbol: string; // Trading symbol (e.g., "BTCUSDT") - NO special characters! name: string; // Display name (e.g., "BTC / USDT PERPETUAL FUTURES") asset_type: AssetType; // Asset type classification source_id: string; // Source identifier from exchange // Symbol Pair Information (for crypto/forex) pair?: { from: string; // Base currency (e.g., "BTC") to: string; // Quote currency (e.g., "USDT") }; // Trading Information tradeable: boolean; // Whether symbol is tradeable is_index: boolean; // Whether symbol is an index is_formula: boolean; // Whether symbol is a formula/calculated // Data Feed Information delay_seconds: number; // Data delay in seconds (0 for real-time) data_status: DataStatus; // Data streaming status data_source_location?: string; // Data source location/datacenter // Display Information industry?: string; // Industry classification symbol_logo_urls?: string[]; // Array of logo URLs // Price & Volume Precision contract_size: number; // Contract size (for futures/options) tick_size: number; // Minimum price movement display_tick_size: number; // Display tick size volume_size_increment: number; // Minimum volume increment max_tick_precision: number; // Maximum decimal places for price max_volume_precision: number; // Maximum decimal places for volume quote_currency: string; // Quote currency // Futures-specific (if applicable) future_type?: string; // 'PERP' for perpetual, or expiry date for dated futures // Exchange Information exchange_info: { name: string; // Exchange name (lowercase) code: string; // Exchange code (uppercase) country_cd: string; // Country code zone: string; // Timezone has_unique_trade_id: boolean; // Whether exchange provides unique trade IDs logo_url: string; // Exchange logo URL holidays: string[] | null; // Array of holiday dates (null if 24/7) hours: Array<{ open: boolean }>; // Trading hours for each day (0=Sunday, 6=Saturday) contains_ambiguous_symbols: boolean; // Whether exchange has ambiguous symbol names valid_intervals: string[]; // Supported timeframes }; // Legacy/Compatibility Fields (still supported) ticker?: string; // Ticker symbol (same as symbol) full_name?: string; // Full symbol name with exchange:segment:symbol description?: string; // Human-readable description (same as name) type?: SymbolType; // Symbol type (derived from asset_type) session?: string; // Trading session timezone?: string; // Timezone (from exchange_info.zone) has_intraday?: boolean; // Supports intraday data has_daily?: boolean; // Supports daily data supported_resolutions?: string[]; // Supported timeframes (from exchange_info.valid_intervals) volume_precision?: number; // Volume decimal places (same as max_volume_precision) } // Asset types type AssetType = "CRYPTO" | "EQUITY" | "FOREX" | "COMMODITY" | "INDEX" | "BOND"; // Symbol types (legacy compatibility) type SymbolType = | "stock" | "forex" | "crypto" | "futures" | "options" | "index" | "bond"; // Data status type DataStatus = "streaming" | "endofday" | "pulsed" | "delayed_streaming"; // Bar/Candle data interface Bar { time: number; // Unix timestamp in milliseconds open: number; // Opening price high: number; // Highest price low: number; // Lowest price close: number; // Closing price volume: number; // Volume } // Real-time tick data interface Tick extends Bar { change?: number; // Price change changePercent?: number; // Price change percentage } // Search result interface SearchResult { symbol: string; // Symbol name full_name: string; // Full symbol name description: string; // Description exchange: string; // Exchange name ticker: string; // Ticker type: SymbolType; // Symbol type } ``` ## Configuration Types ### Available Configuration Options ```typescript // IMPLEMENTED - Current ChartConfig interface // This is the actual interface used by createChart() interface ChartConfig { // Required symbol: string; // Trading symbol interval: string; // Chart interval datafeed: Datafeed; // Your datafeed implementation licenseKey: string; // Your license key // Optional - Display theme?: ChartTheme; // Chart theme (default: "light") autosize?: boolean; // Enable automatic resizing (default: true) width?: number | string; // Chart width (default: "100%") height?: number | string; // Chart height (default: "100%") // Optional - Features debugLog?: boolean; // Enable debug logging (default: false) skipLicenseValidation?: boolean; // Skip license validation (for demos) touchMode?: boolean; // Force full JS mobile Chart-tab shell (default: false) isNativeApp?: boolean; // Native WebView: hide JS bars; emit OPEN_CONTEXT_MENU (default: false) // Optional - Trading trading?: TradingConfig; // Trading configuration (use trading.enableTrading) // Optional - UI Customization exclude?: ExcludeOptions; // Exclude specific UI elements // Event callbacks appCallback?: ( eventType: AppCallbackEventType, message: AppCallbackMessage, onClose?: () => void, ) => void; // Trading and app events onReady?: (chartInstance: ChartInstance) => void; // Chart ready callback onError?: (error: Error | ChartError | string) => void; // Error callback } ``` ## Trading Types ### IMPLEMENTED Trading Features ```typescript // IMPLEMENTED - Trading events through appCallback type TradingEventType = | "CREATE_ORDER" // User initiated order creation | "PLACE_ORDER" // User confirmed order placement | "MODIFY_ORDER" // User requested order modification | "CANCEL_ORDER"; // User requested order cancellation // IMPLEMENTED - Chart method for updating broker data interface ChartInstance { setBrokerAccounts(data: BrokerAccountData): void; // ... other chart methods } ``` ### Trading Data Types ```typescript // Order types type OrderType = "market" | "limit" | "stop" | "stop_limit"; type OrderSide = "buy" | "sell"; type OrderStatus = "pending" | "filled" | "cancelled" | "rejected" | "expired"; type TimeInForce = "GTC" | "IOC" | "FOK" | "DAY"; // Security information object interface SecurityInfo { symbol: string; // Symbol name exchange: string; // Exchange name segment: string; // Market segment tick_size: number; // Minimum price increment lot_size: number; // Minimum quantity increment quote_currency: string; // Quote currency } // Order interface interface Order { // Core Fields orderId: string; // Unique order ID symbol: string; // Trading symbol (base symbol name) side: OrderSide; // Order side (buy/sell) orderType: OrderType; // Order type price: number; // Order price size: number; // Order size/quantity status: OrderStatus; // Order status // Additional Required Fields productId: string; // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) broker: string; // Broker identifier productType: string; // Product type: "FUTURE" | "SPOT" | "OPTION" exchange: string; // Exchange name segment: string; // Market segment currency: string; // Order currency key: string; // Unique key (broker-productId-orderId) isGC: boolean; // GoCharting flag security: SymbolInfo | SecurityInfo; // Security information // Optional Fields timeInForce?: TimeInForce; // Time in force timestamp?: string; // Order timestamp (ISO string) datetime?: Date; // Order creation date timeStamp?: number; // Order timestamp in milliseconds fillPrice?: number | null; // Fill price (if filled) avgFillPrice?: number | null; // Average fill price fillSize?: number; // Filled size filledSize?: number; // Filled size (alias) remainingSize?: number; // Remaining unfilled quantity commission?: number; // Commission/fees commissions?: number; // Commission amount lastTradeTimestamp?: number | null; // Last trade timestamp cost?: number | null; // Order cost trades?: Trade[]; // Associated trades fee?: { // Fee information currency: string; cost: number; rate: number; }; info?: Record; // Additional broker-specific information modifiedAt?: number | null; // Last modification timestamp takeProfit?: number | string | null; // Take profit price stopLoss?: number | string | null; // Stop loss price stopPrice?: number | null; // Stop trigger price (for stop orders) pnlMultiplier?: number; // PnL calculation multiplier (for TP/SL USD calculations) paperTraderKey?: string | null; // Paper trading key validity?: string; // Order validity: "DAY", "GTC", etc. rejReason?: string | null; // Rejection reason userTag?: string | null; // User-defined tag showStopLossButton?: boolean; // Whether to show stop loss button in UI showTakeProfitButton?: boolean; // Whether to show take profit button in UI } // Trade interface interface Trade { // Required Fields tradeId: string; // Unique trade ID symbol: string; // Trading symbol side: OrderSide; // Trade side price: number; // Execution price size: number; // Trade size (quantity) // Optional Fields orderId?: string; // Associated order ID tradeSize?: number; // Trade size (alias for size, used in some contexts) value?: number; // Trade value commission?: number; // Commission/fees timestamp?: string; // Trade timestamp (ISO string) datetime?: Date; // Trade creation date timeStamp?: number; // Trade timestamp in milliseconds productId?: string; // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) exchange?: string; // Exchange name broker?: string; // Broker identifier productType?: string; // Product type: "FUTURE" | "SPOT" | "OPTION" status?: string; // Trade status cost?: number; // Trade cost (price × quantity) fee?: { // Fee information currency: string; cost: number; rate: number; }; security?: SymbolInfo | SecurityInfo; // Security information key?: string; // Unique key (broker-productId-tradeId) } // Position interface interface Position { // Core Fields size: number; // Net position size (positive = long, negative = short) size_currency: number; // Position quantity in base currency price: number; // Average entry price amount: number; // Position value (price × quantity) // Identifiers productId: string; // Full symbol identifier (EXCHANGE:SEGMENT:SYMBOL) broker: string; // Broker identifier productType: string; // Product type: "FUTURE" | "SPOT" | "OPTION" currency: string; // Position currency id: string; // Position identifier underlying: string; // Underlying asset identifier symbol: string; // Base symbol name segment: string; // Market segment exchange: string; // Exchange name key: string; // Unique key (broker-productId-positionId) isGC: boolean; // GoCharting flag // P&L Fields unPnl: number; // Unrealized profit/loss rPnl: number; // Realized profit/loss pnl: number; // Total profit/loss security: SymbolInfo | SecurityInfo; // Security information // Optional Fields pnlMultiplier?: number; // PnL calculation multiplier openPosVal?: number; // Open position value closedPosVal?: number; // Closed position value paperTraderKey?: string | null; // Paper trading key positions?: Record; // Detailed position breakdown (keyed by position ID or symbol) takeProfit?: number | null; // Take profit level stopLoss?: number | null; // Stop loss level trailingSLSpread?: number | null; // Trailing stop loss spread trailingStopHighestPnL?: number | null; // Highest PnL for trailing stop showStopLossButton?: boolean; // Whether to show stop loss button in UI showTakeProfitButton?: boolean; // Whether to show take profit button in UI } // Account interface interface Account { id: string; name: string; balance: number; currency: string; broker?: string; leverage?: number; marginUsed?: number; marginAvailable?: number; } // Broker account data interface BrokerAccountData { accountList: Account[]; orderBook: Order[]; tradeBook: Trade[]; positions: Position[]; } ``` ## � App Callback Types ### Event Types ```typescript // App callback event types - all events that can be sent to appCallback type AppCallbackEventType = // Trading events | "PLACE_ORDER" // User wants to place an order | "MODIFY_ORDER" // User wants to modify an order | "CANCEL_ORDER" // User wants to cancel an order | "MODIFY_POSITION" // User wants to modify a position | "CLOSE_POSITION" // User wants to close a position | "REVERSE_POSITION" // User wants to reverse a position // UI events | "ALERT_TRIGGERED" // Alert was triggered | "DOWNLOAD_DATA" // User wants to download chart data | "TAKE_SCREENSHOT" // User wants to take a screenshot | "SHARE_CHART" // User wants to share the chart | "PRINT_CHART" // User wants to print the chart | "FULLSCREEN_TOGGLE" // User toggled fullscreen | "THEME_CHANGE" // User changed theme | "INTERVAL_CHANGE" // User changed interval | "SYMBOL_CHANGE"; // User changed symbol ``` ### Message Types ```typescript // Place order message interface PlaceOrderMessage { order: Partial; // Order details } // Modify order message interface ModifyOrderMessage { orderId: string; // Order ID to modify updates: Partial; // Fields to update } // Modify position message interface ModifyPositionMessage { position: Position; // Position to modify updates?: { // Optional updates takeProfit?: number | null; stopLoss?: number | null; trailingSLSpread?: number | null; }; } // Alert message interface AlertMessage { alert: { symbol: string; price: number; condition: "above" | "below"; message?: string; }; } // Download data message interface DownloadDataMessage { params: [number, number] | [number, number, unknown]; // [from, to] or [from, to, format] } // UI event message interface UIEventMessage { chartId?: string; data?: any; } // Union type of all possible callback messages type AppCallbackMessage = | PlaceOrderMessage | ModifyOrderMessage | ModifyPositionMessage | AlertMessage | DownloadDataMessage | UIEventMessage; ``` ## Advanced Types ### Chart Widget Instance ```typescript // Low-level chart widget instance (returned by getChartInstance()) interface ChartWidgetInstance { container: HTMLElement; // Chart container element // Additional methods available on the underlying chart instance // (Chart widget instance) } ``` ### Timescale Marks ```typescript // Timescale mark for marking important events on the chart interface TimescaleMark { id: string; // Unique mark ID time: number; // Time coordinate (Unix timestamp) color: string; // Mark color label: string; // Mark label text tooltip?: string; // Tooltip text shape?: "circle" | "square" | "arrowUp" | "arrowDown"; // Mark shape } ``` ### UDF Response Format ```typescript // Universal Data Feed (UDF) response format interface UDFResponse { s: "ok" | "error" | "no_data"; // Status t?: number[]; // Time array (Unix timestamps) o?: number[]; // Open prices h?: number[]; // High prices l?: number[]; // Low prices c?: number[]; // Close prices v?: number[]; // Volume errmsg?: string; // Error message (if s === "error") nextTime?: number; // Next available data time } ``` ### Resolution ```typescript // Chart resolution/interval configuration interface Resolution { scale: number; // Scale value (e.g., 1, 5, 15) unit: "S" | "D" | "W" | "M"; // Time unit: Second, Day, Week, Month text: string; // Display text (e.g., "1m", "5m", "1D") } ``` ## Event Types ### Event Interfaces ```typescript // Chart events interface ChartClickEvent { x: number; y: number; price: number; time: number; ctrlKey?: boolean; shiftKey?: boolean; altKey?: boolean; } interface CrosshairMoveEvent { price: number; time: number; x: number; y: number; } // Error types interface ChartError { type: "license" | "data" | "network" | "general"; message: string; code?: string; details?: any; } // Drawing events interface DrawingObject { id: string; type: string; points: DrawingPoint[]; style: DrawingStyle; } interface DrawingPoint { time: number; price: number; } interface DrawingStyle { color?: string; width?: number; style?: "solid" | "dashed" | "dotted"; } ``` ## Usage Examples ### IMPLEMENTED - Current TypeScript Usage ```typescript // IMPLEMENTED - Use the actual GoCharting SDK API declare global { interface Window { GoChartingSDK: { createChart: ( container: HTMLElement | string, config: ChartConfig, ) => ChartInstance; }; } } // Create a typed datafeed object const myDatafeed: Datafeed = { async getBars( symbolInfo: SymbolInfo, resolution: string, periodParams: PeriodParams, ): Promise { // Implementation const bars: Bar[] = await this.fetchBars( symbolInfo, resolution, periodParams, ); return { bars }; }, resolveSymbol( symbolName: string, onResolve: (symbolInfo: SymbolInfo) => void, onError: (error: string) => void, ): void { // Implementation }, searchSymbols( userInput: string, callback: (symbols: SearchResult[]) => void, ): void { // Implementation }, }; // IMPLEMENTED - Use the actual createChart API const chartConfig: ChartConfig = { symbol: "BYBIT:FUTURE:BTCUSDT", interval: "1m", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", theme: "dark", debugLog: false, trading: { enableTrading: true, }, onReady: (chartInstance: ChartInstance) => { console.log("Chart is ready:", chartInstance); // Set up broker data if needed chartInstance.setBrokerAccounts({ accountList: [], orderBook: [], tradeBook: [], positions: [], }); }, appCallback: ( eventType: AppCallbackEventType, message: AppCallbackMessage, ) => { console.log("Trading event:", eventType, message); }, }; // IMPLEMENTED - Create chart using the actual SDK const chart: ChartInstance = window.GoChartingSDK.createChart( "#chart-container", chartConfig, ); ``` ## Related Documentation - **[Chart API](./chart.md)** - Chart instance methods and configuration - **[Datafeed API](./datafeed.md)** - Custom data sources - **[Configuration API](./configuration.md)** - Chart configuration - **[Events API](./events.md)** - Event handling --- _For TypeScript examples, see the [TypeScript tutorial](../examples/typescript.md)._ File: docs/CHANGELOG.md ================================== # Changelog All notable changes to the GoCharting SDK will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added #### Construct / chrome - **ADDED**: `loading_screen` — overlay colors / text while the chart hydrates - **ADDED**: `custom_font_family` — applied to the chart container - **ADDED**: `compare_symbols` — seed Compare overlays at construct time - **ADDED**: `symbol_search_symbols_types` — type filter chips in symbol search - **ADDED**: `custom_timezones` — merge extra IANA zones into the timezone picker - **ADDED**: `custom_translate_function` — wrap UI strings after `intl` init - **ADDED**: `header_widget_buttons_mode` — `fullsize` / `compact` / `adaptive` top-bar density - **ADDED**: `additional_symbol_info_fields` — extra fields on symbol status / bottom bar - **ADDED**: `symbol_search_complete` — callback after a successful symbol pick - **ADDED**: `save_chart_to_server_callback` — notified when a named layout save completes - **ADDED**: `context_menu.items_processor` / `context_menu.renderer_factory` — post-process or replace context-menu item lists - **ADDED**: `suggested_chart_change_adapter` — gate `setSymbol` / `setInterval` with `canAcceptSuggestedChange` / `onSuggestedChange` - **NOTE**: Chart Y-axis tick labels follow symbol `tick_size` / `max_tick_precision` — not a host-controlled TV `pricescale`. Construct `numeric_formatting` / `custom_formatters` do not restyle the live axis. - **LABS**: Construct chrome/hooks — [`examples/tv-parity-construct-lab.html`](../examples/tv-parity-construct-lab.html) #### Chart adapters & drawings - **ADDED**: `getSeries()` → `ISeriesApi` (style, visibility, `priceScale()`) - **ADDED**: `getStudyById()` → `IStudyApi` (inputs/styles, visibility, `remove`) - **ADDED**: `getPanes()` → `PaneApi[]` (`getMainSeries`, `getSeries()`, height helpers, overlays) - **ADDED**: `selection()` → `IGraphicSelectedApi` (`get` / `set` / `clear` / z-order / properties) - **ADDED**: `PriceScaleApi` / `SeriesPriceScale` / `SeriesPaneApi` — y-scale mode, visibility, price-to-bar ratio (axis tick format follows symbol metadata, not TV `pricescale`) - **ADDED**: `shapesGroupController` — `getGidDescription`, `getGroupVisibility` / `setGroupVisibility`, `getGroupsVisibility` - **ADDED**: Drawing events — `widget.subscribe("drawing_event", …)` and `activeChart().onDrawingEvent()` (`create` / `remove`; payload `DrawingEventParams`) - **LABS**: Series/pane/study/selection/groups/events — [`examples/tv-parity-adapters-lab.html`](../examples/tv-parity-adapters-lab.html) #### Datafeed (Phase 6) - **ADDED (Phase 6 P6-1)**: Datafeed `onReady` → `DatafeedConfiguration` handshake; `chart.getDatafeedConfiguration()`; `getIntervals()` falls back to `supported_resolutions`. Lab: [`examples/phase6-onReady-udf-lab.html`](../examples/phase6-onReady-udf-lab.html) - **ADDED (Phase 6 UDF)**: `UDFCompatibleDatafeed` — HTTP UDF client (`/config`, `/symbols`, `/history`, `/search`, optional marks/time). Same lab as P6-1. - **ADDED (Phase 6 P6-2)**: Datafeed `subscribeBars` / `unsubscribeBars` — host pushes complete OHLCV; SDK applies without tick aggregation; mutually exclusive with ticks (ticks preferred if both). Lab: [`examples/phase6-subscribeBars-lab.html`](../examples/phase6-subscribeBars-lab.html) - **ADDED (Phase 6 P6-5 partial)**: Datafeed `searchSymbolsPaginated` — preferred over `searchSymbols` for chart search (first page). Lab: [`examples/phase6-searchSymbolsPaginated-lab.html`](../examples/phase6-searchSymbolsPaginated-lab.html) - **DOCS**: TV → GC migration + LLM context — near–full widget/chart/datafeed parity notes; **known limitation**: no TV-style client aggregation of `2D` / `6M` / … from daily (`daily_multipliers`); host must return bars for the requested resolution - **FIXED**: UDF `session` (e.g. `0930-1630`) mapped into `exchange_info.hours` so market status respects open/closed; equities without intraday use `endofday` data status - **IMPROVED**: `getMarks` rendered as **price marks** (near bar highs); `getTimescaleMarks` auto-fetched and kept on the timescale; fixed FUNDAMENTALS `productId` key ## [1.0.69] - 2026-07-18 Differentiated `isNativeApp` from `touchMode` for native WebView hosts, with a bidirectional bridge guide. ### Added #### Widget façade (chart API) - **ADDED**: `activeChart()` / `chart(index)` → `IChartApi` (getters + existing mutators bound by index) - **ADDED**: `chartsCount()`, `activeChartIndex()`, `setActiveChart(index)` - **ADDED**: `subscribe` / `unsubscribe` event bus (`activeChartChanged`, `layout`, plus raw `appCallback` event types) - **ADDED**: `getAllCharts()` on the `createChart` wrapper - **ADDED (Phase 1A)**: `IChartApi` viewport APIs — `getVisibleRange` / `setVisibleRange` (Unix seconds), `getVisibleBarsRange`, `goToDate`, `setTimeFrame`, `zoomOut`, `canZoomOut`, `resetData`, `exportData`, `getPanes`, `getAllPanesHeight`, `getTimeScale`, `getSeries`, `priceFormatter`, `maximizeChart` / `isMaximized`, `symbolExt`, `marketStatus`, `inactivityGaps` - **ADDED (Phase 1B)**: `IChartApi` shapes APIs — `createShape` / `createMultipointShape` / `createAnchoredShape` (return `objectId`; GC `shape`/`options`/`appearance`), `getAllShapes` / `getShapeById`, `removeEntity` / `removeAllShapes`, `getLineToolsState` / `applyLineToolsState`, `reloadLineToolsFromServer` (stub until Phase 5 — throws), z-order (`bringToFront`/`bringForward`/`sendToBack`/`sendBackward` + `availableZOrderOperations`), `selection`, `showPropertiesDialog`, `shapesGroupController` - **ADDED (Phase 1C)**: `IChartApi` studies APIs — `getAllStudies` / `getStudyById` / `removeAllStudies`, `createStudyTemplate` / `applyStudyTemplate` / `loadChartTemplate` (favourite name, study template, or `getChartState` blob); `createStudyTemplate` persists to Templates menu (`favourite.TEMPLATES`) by default (`{ name, save: false }` for snapshot only); `deleteObject` / `removeEntity` resolve studies/drawings across panes and throw if missing - **ADDED (Phase 1D)**: `IChartApi` library-native trading lines — `createOrderLine` / `createPositionLine` / `createExecutionShape` (independent of Broker / `setBrokerAccounts`; tagged `options.source: "library"` so broker book refresh does not wipe them); setters preserve `forTicker` so lines stay visible after updates - **ADDED (Phase 1E)**: `IChartApi` events + actions — `onSymbolChanged` / `onIntervalChanged` / `onChartTypeChanged` / `onDataLoaded` / `dataReady` / `onVisibleRangeChanged` / `crossHairMoved` / `onHoveredSourceChanged`; `executeActionById` allowlist (`chartReset`, `zoomIn`/`Out`, `undo`/`redo`, `invertScale`/`logScale`, `magnet`); `requestSelectBar` / `cancelSelectBar` - **ADDED (Phase 2 P2-1)**: `layout()` / `setLayout(layout)` on the widget wrapper; `CHART_MODE_CHANGED` now emitted on layout change (subscribe `"layout"`); GC layout ids + TV aliases (`s`, `2h`, `2v`, …). `layoutName()` deferred to Phase 5 (no stub shipped) - **ADDED (Phase 2 P2-3)**: `symbolSync()` / `intervalSync()` → `ISyncApi`; `setValue(true)` fans out like layout UI - **ADDED (Phase 2 P2-5)**: `drawOnAllChartsEnabled()` → `ISyncApi` over `layoutStore.syncDrawings` - **ADDED (Phase 2 P2-4)**: `crosshairSync()` / `dateRangeSync()` / `timeSync()` → `ISyncApi` over `syncCursors` / `syncDateRange` / `syncTime` - **ADDED (Phase 2 P2-2)**: `setLayoutSizes({ rows?, columns? })` / `resetLayoutSizes()` — pane % over `sessionStorage` `chartLayoutState` (same as drag-resize) - **ADDED (Phase 2 P2-6)**: `unloadUnusedCharts()` — dispose soft-stashed orphans after layout shrink; clear observers beyond `chartsCount()`; flush session `CompleteChartLayout` - **ADDED (Phase 3 P3-1)**: `applyOverrides` / `applyStudiesOverrides` / `applyTradingCustomization` — GC nested-object merges over chart appearance, study templates, and root `trading` - **ADDED (Phase 3 P3-2)**: `setCSSCustomProperty` / `getCSSCustomPropertyValue` / `addCustomCSSFile` / `customThemes()` — CSS vars + stylesheet inject on chart container (canvas still uses `setTheme` / `applyOverrides`) - **ADDED (Phase 3 P3-3)**: `features()` / `setFeatureEnabled` / `getFeatureEnabled` / `getFeatures()` — runtime chrome toggles (`topBar`, `drawingToolbar`, `symbolSearch`, `compare`, …) beyond static `exclude`; TV aliases (`header_widget`, …) - **ADDED (Phase 4 P4-1)**: Construct-time `disabled_features` / `enabled_features` string lists seed `exclude` / `disableSearch` / `disableCompare` / `hideDrawingToolBar` before mount; expanded TV aliases; unknown strings warn once and stay on config; lab [`examples/phase4-constructor-lab.html`](./examples/phase4-constructor-lab.html) - **ADDED (Phase 4 P4-2)**: Construct-time `overrides` / `studies_overrides` / `settings_overrides` — appearance/settings merge into `defaultInitialChartConfig` pre-store; re-apply via `applyOverrides` / `applyStudiesOverrides` after charts exist - **ADDED (Phase 4 P4-3)**: `custom_css_url` → `addCustomCSSFile`; `custom_themes` → façade; `timezone` → `settings.zone`; store-only `custom_font_family` / `custom_formatters` / `custom_timezones` / `custom_translate_function` - **ADDED (Phase 4 P4-4)**: Soft `studies_access` blacklist → `exclude.indicators` / `getStudiesList`; soft `drawings_access` + `study_count_limit` warn (hard block deferred) - **ADDED (Phase 4 P4-5)**: TV `favorites` → GC `favourite`; `time_frames` filters `getIntervals`; `timeframe` post-init `setTimeFrame`; `timezone` shared with P4-3 - **ADDED (Phase 4 P4-6)**: `toolbar_bg` CSS var; `context_menu` alias of `contextMenu`; `symbol_search_request_delay` debounces symbol search - **ADDED (Phase 4 P4-7)**: Adapter option shapes on config (`save_load_adapter`, `settings_adapter`, `image_storage_adapter`, `charts_storage_*`, `client_id`, `user_id`) — passthrough for Phase 5 behavior - **ADDED (Phase 5 P5-1)**: Widget `save()` / `load()` — GC-native layout blob (`kind: "gocharting-layout"`) from CompleteChartLayout + per-chart `getChartState` - **ADDED (Phase 5 P5-2)**: `getSavedCharts` / `saveChartToServer` / `loadChartFromServer` / `removeChartFromServer` via `save_load_adapter` (localStorage default when unset; optional `charts_storage_url` HTTP) - **ADDED (Phase 5 P5-3)**: `settings_adapter` hydrate for `themeColor` / `favourite` / feature seeds around store create - **ADDED (Phase 5 P5-4)**: `layoutName()` / `showSaveAsChartDialog` / `showLoadChartDialog` - **ADDED (Phase 5 P5-5)**: `takeScreenshot` prefers `image_storage_adapter.upload` over `snapshot_url` - **ADDED**: Example lab [`examples/phase5-persistence-lab.html`](./examples/phase5-persistence-lab.html) - **ADDED (Phase 3 P3-4)**: `headerReady()` / `createButton` / `createDropdown` / `removeButton` — custom top-toolbar controls (DOM button + React dropdown); prefer `headerReady().then(...)` before `createButton` - **ADDED (Phase 3 P3-5)**: `onShortcut` / `onContextMenu` — keyboard shortcuts (`"alt+q"` or `["alt", 81]`) and chart context-menu injection (`{ text, click, position }`; `"-"` separator; `"-Label"` hide built-in) - **ADDED (Phase 3 P3-6)**: `closePopupsAndDialogs` / `showConfirmDialog` / `showNoticeDialog` — dismiss menus/portals/popups; ConfirmAlerts-based confirm/notice (Promise + TV object form) - **ADDED (Phase 3 P3-7)**: `selectLineTool` / `selectedLineTool` / `hideAllDrawingTools` / `lockAllDrawingTools` / `magnetEnabled` / `magnetMode` — drawing toolbar state; hide/lock/magnet as `ISyncApi`; `magnetMode` is 0/1 stub (no weak/strong) - **ADDED (Phase 3 P3-8)**: `takeClientScreenshot` / `takeScreenshot` — local canvas (`getChartImage` → html2canvas); upload via `snapshot_url` (multipart `preparedImage`); emits `onScreenshotReady` - **ADDED**: Top-bar camera button — capture with ChartBrandLogo composite; dropdown for Download PNG / Copy to clipboard (`exclude.screenshot` to hide) - **ADDED (Phase 3 P3-9)**: `clearUndoHistory` / `undoRedoState` / `resetCache` — clear global undo stack; UndoRedoState snapshot (scope labels); invalidate bar cache (`lastBarsCache` + `datafeed.resetCache` + subscribe callbacks); emits `undo_redo_state_changed` - **ADDED (Phase 3 P3-10)**: `getStudiesList` / `getStudyInputs` / `getStudyStyles` — indicator catalog metadata (`type` ids for `addIndicator`); inputs from `computationFields`; styles from `appearanceFields` - **ADDED (Phase 3 P3-11)**: `supportedChartTypes` / `getIntervals` / widget `symbolInterval` — GC chart type ids (TopBar filter); interval list from `BASE_INTERVALS` or active `valid_intervals`; active chart `{ symbol, interval }` - **ADDED (Phase 3 P3-12)**: `mainSeriesPriceFormatter` / `currencyAndUnitVisibility` / `customSymbolStatus` — active-series price format (`max_tick_precision`); visibility watched-value shell; fluent custom status store (UI paint deferred) - **ADDED (Phase 3 P3-13)**: `navigationButtonsVisibility` / `paneButtonsVisibility` / `dateFormat` / `timeHoursFormat` — watched values wired to `settings.navigationButton` / `paneButton` / `xAxis.timeScale` (live UI) - **FIXED**: Settings `dateFormat` / `dayOfWeekOnLabels` / `timeHoursFormat` now apply to axis month labels and mouse X / crosshair (chart-widget previously ignored them for month ticks and used hardcoded `ccc dd LLL yy`) - **FIXED**: Multi-chart symbol change no longer mixes instruments (e.g. ETH LTP on BTC candles). Root cause: SDK `TimeSeriesFeedSDK` applied every `TS/V2` history response to every feed (`originating_request_id` check was disabled). Also harden `setSymbol` (clone + new ids, `CLEAR_DATA`, ChartWidget series invalidate) and DataProvider pipe/unpipe - **CHANGED**: Façade payloads use **GoCharting field names** — `symbolExt()` → `max_tick_precision` / `tick_size` (not TV `pricescale` / `minmov`); `getPanes()` → `{ idx, id, heightFraction }` (not `paneIndex` / `chartId`); `marketStatus()` → `{ isOpen, statusText, dataStatus, delaySeconds }`; prefer `interval()` / `setInterval()` over `resolution()` / `setResolution()` aliases - **DOCS**: Chart API — Widget façade section fully documented for Phase 0 / 1A / 1B / 1C / 1D / 1E / Phase 2 / P3-1 … P3-13 (nextra + `docs/api/chart.md`); types for `IChartApi` / `ISyncApi` / `INumberSyncApi` / `ClientSnapshotOptions` / `UndoRedoState` / `StudyInputInformation` / `StudyStyleInfo` / `SymbolIntervalResult` / `PriceFormatter` / `IVisibilityApi` / `ICustomSymbolStatusApi` / `IVisibilityWatchedValue` / `IStringWatchedValue` / `LayoutSizes` / `ApplyOverridesInput` / `ICustomThemesApi` / `IFeaturesApi` / `IDropdownApi` / `ContextMenuItem` / `ConfirmDialogParams` / shapes / studies / trading lines / events / viewport (nextra + `docs/api/types.md`) - **ADDED**: Example lab [`examples/phase1c-api-lab.html`](./examples/phase1c-api-lab.html) - **ADDED**: Example lab [`examples/phase1d-api-lab.html`](./examples/phase1d-api-lab.html) - **ADDED**: Example lab [`examples/phase1e-api-lab.html`](./examples/phase1e-api-lab.html) - **ADDED**: Example lab [`examples/phase2-layout-lab.html`](./examples/phase2-layout-lab.html) - **ADDED**: Example lab [`examples/phase3-overrides-lab.html`](./examples/phase3-overrides-lab.html) #### Native WebView bridge - **ADDED**: `OPEN_CONTEXT_MENU` `appCallback` event when `isNativeApp` is true (JS ContextMenu suppressed; payload includes `chartId`, `details`, `targetType`, `objectId`, `x`/`y`, `security`, `interval`) - **ADDED**: `sendToNative()` / `sendAppCallbackToNative()` — every `TerminalMobileChart` `appCallback` posts `{ type, ...msg }` to React Native (`ReactNativeWebView`), Flutter (`Flutter`), Android (`Android`), and iOS (`webkit.messageHandlers.ios`) - **ADDED**: [Mobile Integration](./guides/mobile-integration.md) guide — WebView bootstrap, message shapes, receive/inject snippets for RN / Flutter / Android / iOS ### Changed - **CHANGED**: `isNativeApp` now hides JS TopBar/BottomBar (host owns chrome) while keeping the mobile chart canvas and Layers sheets - **CHANGED**: `touchMode` remains the full JS mobile Chart-tab shell (bars + ContextMenu + Layers) - **BREAKING / CLEANUP**: Root `enableTrading` prop removed; use `trading.enableTrading` only - **FIXED**: createChart props (`trading`, `defaultInitialChartConfig`, `theme`, `exclude`) now win over `localStorage` / `sessionStorage` after hydrate — applied onto hydrated state **before** store create, persisted immediately, and re-applied after chart init so session chart configs cannot stick - **FIXED**: `sendToNative` no longer crashes on pan/zoom — skips stringify when no native bridge is present, uses cycle-safe serialization, and omits heavy `SAVE_CONFIGURATION_STORE` chart-state payloads ```javascript const chart = GoChartingSDK.createChart("#chart-container", { symbol: "AAPL", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", isNativeApp: true, // canvas only; host chrome + sendToNative bridge appCallback: (type, msg) => { // Also delivered automatically via sendToNative() if (type === "OPEN_CONTEXT_MENU") { // show native menu at msg.x, msg.y } }, }); ``` ### Documentation - **UPDATED**: Configuration API (`isNativeApp` / `touchMode`), GoCharting props, Advanced Features - **ADDED**: Guides → [Mobile Integration](./guides/mobile-integration.md) - **DOCS**: Documented Phase 0 / 1A / 1B Widget façade fully in [Chart API](./api/chart.md) + [Types](./api/types.md) (incl. `getAllPanesHeight`, `reloadLineToolsFromServer` stub) --- ## [1.0.68] - 2026-07-17 Mobile chart shell for embeds and native hosts, plus Layers UI parity on touch devices. ### Added #### Mobile chart shell (`TerminalMobileChart`) - **ADDED**: Chart-tab mobile layout for SDK embeds — mirrors production TerminalMobile **Chart** tab content (mobile top bar → chart → chart tool bottom bar + Layers sheets) **without** the TerminalMobile app BottomNav (Watchlist / Portfolio / Widgets / Account) - **ADDED**: `touchMode` and `isNativeApp` now activate that shell (previously documented but not fully wired). Also activates automatically when `isMobileFunc()` detects a mobile/touch host - **ADDED**: `createChart` / `` set `window.__GOCHARTING_TOUCH_MODE__` when `touchMode` or `isNativeApp` is true (cleared on destroy / unmount) - **ADDED**: Mobile Layers bottom sheets (`LayersModal`, `LayersObjectModal`) with touch-friendly styling via `MobileLayersScope` ```javascript const chart = GoChartingSDK.createChart("#chart-container", { symbol: "AAPL", interval: "1D", datafeed: myDatafeed, licenseKey: "YOUR_LICENSE_KEY", touchMode: true, // force full JS mobile Chart-tab shell }); ``` > **Note (1.0.69):** `isNativeApp` is no longer identical to `touchMode` — see [1.0.69](#1069---2026-07-18) for native WebView chrome + `sendToNative`. ### Changed - **CHANGED**: `touchMode` / `isNativeApp` description — they no longer mean “hide all chrome”; they switch to the mobile chart shell (symbol/tools chrome included, app tab nav excluded). Refined further in 1.0.69. ### Documentation - **UPDATED**: Configuration API, GoCharting component props, Advanced Features, and Changelog for the mobile chart shell --- ## [1.0.67] - 2026-07-16 Terminal UI parity with deployed production, trading-event completeness, and a round of interaction fixes across menus, the color picker, and the market-status indicator. ### Added #### Trading - **ADDED**: `CLOSE_POSITION` app-callback event — clicking the **X** on a chart position line now forwards a `CLOSE_POSITION` event (`{ position, chartIdx }`) to your `appCallback`, so hosts can close the position and clear the widget's processing state - **ADDED**: Working multi-chart layouts — all 1–4 chart layout presets in the layout chooser now apply (the 4-chart presets were previously gated behind a premium check that no-oped in the SDK) ### Fixed #### Menus & Popups - **FIXED**: Symbol search results now render above the Compare modal overlay (were hidden behind it) and the dropdown is stable while typing — instant "Searching…" feedback, no flicker/jump, stale responses dropped, and the list no longer collapses on a second click - **FIXED**: Study (Indicators) menu submenus no longer flash at the top-left on first open and are clamped correctly inside embedded widgets - **FIXED**: Timezone drop-up `
Loading chart...
{{ (status$ | async)?.error }}
{{ (status$ | async)?.message }}
`, styleUrls: ["./trading-chart.component.scss"], changeDetection: ChangeDetectionStrategy.OnPush, }) export class TradingChartComponent implements OnInit, OnDestroy { @ViewChild("chartContainer", { static: true }) chartContainer!: ElementRef; @Input() symbol: string = "NASDAQ:AAPL"; @Input() interval: string = "1D"; @Input() theme: "light" | "dark" = "dark"; @Input() height: number = 500; @Input() showControls: boolean = true; @Input() licenseKey: string = ""; @Output() chartReady = new EventEmitter(); @Output() symbolChange = new EventEmitter(); @Output() intervalChange = new EventEmitter(); @Output() error = new EventEmitter(); controlsForm: FormGroup; status$: Observable; config$: Observable; private destroy$ = new Subject(); constructor(private chartService: ChartService, private fb: FormBuilder) { this.controlsForm = this.fb.group({ symbol: [this.symbol], interval: [this.interval], theme: [this.theme], }); this.status$ = this.chartService.status$; this.config$ = this.chartService.config$; } ngOnInit(): void { // Initialize chart this.initializeChart(); // Subscribe to config changes this.config$.pipe(takeUntil(this.destroy$)).subscribe((config) => { this.controlsForm.patchValue(config, { emitEvent: false }); }); // Subscribe to status changes for events this.status$.pipe(takeUntil(this.destroy$)).subscribe((status) => { if ( status.type === "success" && status.message.includes("Chart ready") ) { this.chartReady.emit(); } if (status.error) { this.error.emit(status.error); } }); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } private async initializeChart(): Promise { try { await this.chartService.initializeChart( this.chartContainer.nativeElement, this.licenseKey, ); } catch (error) { console.error("Failed to initialize chart:", error); } } async onSymbolChange(): Promise { const symbol = this.controlsForm.get("symbol")?.value; if (symbol) { await this.chartService.changeSymbol(symbol); this.symbolChange.emit({ name: symbol }); } } async onIntervalChange(): Promise { const interval = this.controlsForm.get("interval")?.value; if (interval) { await this.chartService.changeInterval(interval); this.intervalChange.emit(interval); } } onThemeChange(): void { const theme = this.controlsForm.get("theme")?.value; if (theme) { this.chartService.changeTheme(theme); } } } ``` ## Component Styles ```scss // components/trading-chart.component.scss .trading-chart-container { width: 100%; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } .chart-controls { margin-bottom: 1rem; padding: 1rem; background: #f8f9fa; border-radius: 8px; border: 1px solid #e1e5e9; } .controls-form { display: flex; gap: 1rem; flex-wrap: wrap; align-items: end; } .control-group { display: flex; flex-direction: column; gap: 0.5rem; min-width: 120px; label { font-weight: 600; font-size: 0.9rem; color: #495057; margin-bottom: 0.25rem; } select { padding: 0.5rem; border: 1px solid #ced4da; border-radius: 4px; background: white; font-size: 0.9rem; cursor: pointer; transition: border-color 0.3s ease; &:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); } &:hover { border-color: #adb5bd; } } } .chart-container { width: 100%; border: 1px solid #e1e5e9; border-radius: 8px; position: relative; background: #ffffff; overflow: hidden; } .loading { display: flex; align-items: center; justify-content: center; height: 100%; font-size: 1.1rem; color: #6c757d; gap: 1rem; .spinner { width: 24px; height: 24px; border: 3px solid #e1e5e9; border-top: 3px solid #007bff; border-radius: 50%; animation: spin 1s linear infinite; } } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .error { display: flex; align-items: center; justify-content: center; height: 100%; font-size: 1.1rem; color: #dc3545; background: #f8d7da; padding: 1rem; text-align: center; } .status { margin-top: 1rem; padding: 0.75rem; border-radius: 4px; font-size: 0.9rem; text-align: center; transition: all 0.3s ease; &.info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; } &.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; } &.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } } // Responsive design @media (max-width: 768px) { .controls-form { flex-direction: column; gap: 0.75rem; } .control-group { width: 100%; min-width: unset; } .chart-container { min-height: 300px; } } // Dark theme support :host-context(.dark-theme) { .chart-controls { background: #2d3748; border-color: #4a5568; } .control-group label { color: #e2e8f0; } .control-group select { background: #1a202c; border-color: #4a5568; color: #e2e8f0; &:focus { border-color: #63b3ed; box-shadow: 0 0 0 2px rgba(99, 179, 237, 0.25); } } .chart-container { background: #1a202c; border-color: #4a5568; } .loading { color: #a0aec0; } } ``` ## Module Setup ```typescript // app.module.ts import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; import { ReactiveFormsModule } from "@angular/forms"; import { AppComponent } from "./app.component"; import { TradingChartComponent } from "./components/trading-chart.component"; import { ChartService } from "./services/chart.service"; @NgModule({ declarations: [AppComponent, TradingChartComponent], imports: [BrowserModule, ReactiveFormsModule], providers: [ChartService], bootstrap: [AppComponent], }) export class AppModule {} ``` ## Usage in App Component ```typescript // app.component.ts import { Component } from "@angular/core"; @Component({ selector: "app-root", template: `

My Angular Trading App

`, styles: [ ` .app { padding: 2rem; max-width: 1200px; margin: 0 auto; } h1 { color: #333; margin-bottom: 2rem; } app-trading-chart { display: block; margin-bottom: 2rem; } `, ], }) export class AppComponent { licenseKey = "your-license-key-here"; onChartReady(): void { console.log("Chart is ready!"); } onSymbolChange(symbolInfo: any): void { console.log("Symbol changed:", symbolInfo); } onIntervalChange(interval: string): void { console.log("Interval changed:", interval); } onChartError(error: any): void { console.error("Chart error:", error); } } ``` ## Advanced Features ### Custom Chart Guard ```typescript // guards/chart.guard.ts import { Injectable } from "@angular/core"; import { CanActivate, Router } from "@angular/router"; @Injectable({ providedIn: "root", }) export class ChartGuard implements CanActivate { constructor(private router: Router) {} canActivate(): boolean { // Check if license key is available const licenseKey = localStorage.getItem("chartLicenseKey"); if (!licenseKey) { this.router.navigate(["/license-setup"]); return false; } return true; } } ``` ### Chart Configuration Service ```typescript // services/chart-config.service.ts import { Injectable } from "@angular/core"; import { BehaviorSubject } from "rxjs"; export interface GlobalChartConfig { defaultSymbol: string; defaultInterval: string; defaultTheme: "light" | "dark"; licenseKey: string; trading: { enableTrading: boolean; }; } @Injectable({ providedIn: "root", }) export class ChartConfigService { private configSubject = new BehaviorSubject({ defaultSymbol: "NASDAQ:AAPL", defaultInterval: "1D", defaultTheme: "dark", licenseKey: "", trading: { enableTrading: false, }, }); public config$ = this.configSubject.asObservable(); updateConfig(config: Partial): void { const currentConfig = this.configSubject.value; this.configSubject.next({ ...currentConfig, ...config }); // Save to localStorage localStorage.setItem( "chartConfig", JSON.stringify(this.configSubject.value), ); } loadConfig(): void { const saved = localStorage.getItem("chartConfig"); if (saved) { try { const config = JSON.parse(saved); this.configSubject.next(config); } catch (error) { console.warn("Failed to load chart config from localStorage"); } } } } ``` ### Multi-Chart Component ```typescript // components/multi-chart.component.ts import { Component, OnInit } from "@angular/core"; import { ChartConfigService } from "../services/chart-config.service"; @Component({ selector: "app-multi-chart", template: `

{{ chart.title }}

`, styles: [ ` .multi-chart-container { padding: 1rem; } .chart-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); gap: 2rem; } .chart-item { border: 1px solid #e1e5e9; border-radius: 8px; padding: 1rem; background: white; } .chart-item h3 { margin: 0 0 1rem 0; color: #333; font-size: 1.1rem; } `, ], }) export class MultiChartComponent implements OnInit { licenseKey = "your-license-key-here"; charts = [ { id: 1, title: "Apple Inc.", symbol: "NASDAQ:AAPL", interval: "1D", theme: "dark" as const, }, { id: 2, title: "Microsoft Corp.", symbol: "NASDAQ:MSFT", interval: "1D", theme: "light" as const, }, { id: 3, title: "Bitcoin", symbol: "BINANCE:BTCUSDT", interval: "1H", theme: "dark" as const, }, { id: 4, title: "Google", symbol: "NASDAQ:GOOGL", interval: "1D", theme: "light" as const, }, ]; constructor(private configService: ChartConfigService) {} ngOnInit(): void { this.configService.config$.subscribe((config) => { this.licenseKey = config.licenseKey; }); } trackByChart(index: number, chart: any): number { return chart.id; } } ``` ## Responsive Design ### Mobile-Optimized Component ```typescript // components/mobile-chart.component.ts import { Component, OnInit, OnDestroy, HostListener } from "@angular/core"; import { Subject, takeUntil } from "rxjs"; @Component({ selector: "app-mobile-chart", template: `
`, styles: [ ` .mobile-chart-wrapper { position: relative; width: 100%; } .mobile-controls { position: absolute; top: 10px; right: 10px; z-index: 1000; } .controls-toggle { background: #007bff; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; } .mobile-controls-panel { position: absolute; top: 100%; right: 0; background: white; border: 1px solid #ccc; border-radius: 4px; padding: 1rem; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; gap: 0.5rem; min-width: 150px; } .mobile-controls-panel select { padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; } `, ], }) export class MobileChartComponent implements OnInit, OnDestroy { licenseKey = "your-license-key-here"; currentSymbol = "NASDAQ:AAPL"; currentInterval = "1D"; isMobile = false; showMobileControls = false; chartHeight = 500; private destroy$ = new Subject(); @HostListener("window:resize", ["$event"]) onResize(): void { this.updateMobileState(); } ngOnInit(): void { this.updateMobileState(); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } private updateMobileState(): void { this.isMobile = window.innerWidth < 768; this.chartHeight = this.isMobile ? 300 : 500; } toggleControls(): void { this.showMobileControls = !this.showMobileControls; } onSymbolChange(): void { // Symbol change handled by component binding } onIntervalChange(): void { // Interval change handled by component binding } } ``` ## Related Documentation - **[CodePen Live Demo](https://codepen.io/Admin-GoCharting/pen/angular-demo)** - Try the Angular example - **[Quick Start Guide](../tutorials/quick-start.md)** - Get started with the SDK - **[Vue.js Integration](./vue-integration.md)** - Vue.js component examples - **[API Reference](../api/chart.md)** - Complete API documentation - **[Configuration Guide](../api/configuration.md)** - All configuration options ## Troubleshooting ### Common Issues **Chart not rendering:** - Ensure the ViewChild is properly initialized - Check that the component is mounted before initializing - Verify the license key is valid - Make sure the container element has proper dimensions **TypeScript errors:** - Install proper type definitions: `npm install @types/node` - Ensure Angular and TypeScript versions are compatible - Check that all imports are correctly typed **Service injection issues:** - Verify the service is provided in the module or component - Check that the service is properly imported - Ensure singleton behavior with `providedIn: 'root'` **Memory leaks:** - Always destroy charts in `ngOnDestroy` - Unsubscribe from observables using `takeUntil` - Clear any intervals or timeouts **Reactive forms not working:** - Import `ReactiveFormsModule` in your module - Ensure form controls are properly initialized - Check that form validation is set up correctly --- _Ready to use Angular with GoCharting SDK in production? Get your license key at [gocharting.com](https://gocharting.com)!_ File: docs/examples/codepen-embeds.md ================================================ # CodePen Live Examples Interactive examples demonstrating GoCharting SDK features in real-time. ## Live Embedded Examples ### Advanced Trading Demo Complete trading interface with order management, indicators, and drawing tools. **Features Demonstrated:** - Professional candlestick charts - Technical indicators (RSI, MACD, Bollinger Bands) - Drawing tools and annotations - Order management interface - Real-time data simulation - Interactive trading controls [ Open in CodePen](https://codepen.io/Admin-GoCharting/pen/xbwvBbe) | [ Full Screen](https://codepen.io/Admin-GoCharting/full/xbwvBbe) --- ### Basic Charting Demo Simple chart implementation perfect for getting started. **Features Demonstrated:** - Basic chart setup - Symbol switching - Timeframe selection - Theme switching (light/dark) - Chart type selection - Simple configuration [ Open in CodePen](https://codepen.io/Admin-GoCharting/pen/jEypYrM) | [ Full Screen](https://codepen.io/Admin-GoCharting/full/jEypYrM) --- ### Vue.js Integration Demo Vue.js component implementation with reactive data and composition API. **Features Demonstrated:** - Vue 3 Composition API - Reactive chart configuration - Component lifecycle management - Event handling with Vue methods - Responsive Vue component - Vue-style template syntax [ Open in CodePen](https://codepen.io/Admin-GoCharting/pen/vue-demo) | [ Full Screen](https://codepen.io/Admin-GoCharting/full/vue-demo) --- ### Angular Integration Demo Angular component implementation with services, dependency injection, and TypeScript. **Features Demonstrated:** - Angular 15+ with TypeScript - Service-based architecture - Dependency injection patterns - Component lifecycle hooks - Reactive forms integration - Angular event handling [ Open in CodePen](https://codepen.io/Admin-GoCharting/pen/angular-demo) | [ Full Screen](https://codepen.io/Admin-GoCharting/full/angular-demo) --- ### TypeScript Integration Demo Pure TypeScript implementation with classes, interfaces, and modern ES6+ features. **Features Demonstrated:** - TypeScript 5.0+ with strict mode - Class-based architecture - Type safety and interfaces - Generic types and decorators - Module system and imports - Async/await patterns [ Open in CodePen](https://codepen.io/Admin-GoCharting/pen/typescript-demo) | [ Full Screen](https://codepen.io/Admin-GoCharting/full/typescript-demo) --- ### Internationalization & Theme Demo Complete demonstration of internationalization (i18n) with 19+ languages and dynamic theme switching. **Features Demonstrated:** - **19+ Language Support**: English, Chinese (Simplified & Traditional), Japanese, Korean, French, German, Spanish, Portuguese, Italian, Russian, Polish, Turkish, Thai, Vietnamese, Indonesian, Malay, Arabic, Hebrew - **Dynamic Theme Switching**: Switch between light and dark themes using `setTheme()` method - **Real-time Language Switching**: Change UI language without page reload - **Interactive Locale Switcher**: Flag buttons for easy language selection - **Comprehensive Documentation**: Locale codes and usage examples - **Instant Updates**: See changes immediately in the chart UI [ Open in CodePen](https://codepen.io/Admin-GoCharting/pen/ZYWgxev) | [ Full Screen](https://codepen.io/Admin-GoCharting/full/ZYWgxev) --- ## Alternative Embed Options ### For GitHub/GitLab (No iframe support) If you're viewing this on GitHub or another platform that doesn't support iframes, use these preview links: #### Advanced Trading Demo [![Advanced Trading Demo](https://img.shields.io/badge/CodePen-Advanced%20Demo-000000?style=for-the-badge&logo=codepen&logoColor=white)](https://codepen.io/Admin-GoCharting/full/xbwvBbe) #### Basic Charting Demo [![Basic Charting Demo](https://img.shields.io/badge/CodePen-Basic%20Demo-000000?style=for-the-badge&logo=codepen&logoColor=white)](https://codepen.io/Admin-GoCharting/full/jEypYrM) #### Vue.js Integration Demo [![Vue.js Integration Demo](https://img.shields.io/badge/CodePen-Vue.js%20Demo-4FC08D?style=for-the-badge&logo=vue.js&logoColor=white)](https://codepen.io/Admin-GoCharting/full/vue-demo) #### Angular Integration Demo [![Angular Integration Demo](https://img.shields.io/badge/CodePen-Angular%20Demo-DD0031?style=for-the-badge&logo=angular&logoColor=white)](https://codepen.io/Admin-GoCharting/full/angular-demo) #### TypeScript Integration Demo [![TypeScript Integration Demo](https://img.shields.io/badge/CodePen-TypeScript%20Demo-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://codepen.io/Admin-GoCharting/full/typescript-demo) #### Internationalization & Theme Demo [![Internationalization & Theme Demo](https://img.shields.io/badge/CodePen-i18n%20%26%20Theme%20Demo-FF6B6B?style=for-the-badge&logo=codepen&logoColor=white)](https://codepen.io/Admin-GoCharting/full/ZYWgxev) ### Preview Images #### Advanced Trading Demo [![Advanced Trading Demo Preview](https://codepen.io/Admin-GoCharting/pen/xbwvBbe/image/large.png)](https://codepen.io/Admin-GoCharting/full/xbwvBbe) #### Basic Charting Demo [![Basic Charting Demo Preview](https://codepen.io/Admin-GoCharting/pen/jEypYrM/image/large.png)](https://codepen.io/Admin-GoCharting/full/jEypYrM) #### Vue.js Integration Demo [![Vue.js Integration Demo Preview](https://codepen.io/Admin-GoCharting/pen/vue-demo/image/large.png)](https://codepen.io/Admin-GoCharting/full/vue-demo) #### Angular Integration Demo [![Angular Integration Demo Preview](https://codepen.io/Admin-GoCharting/pen/angular-demo/image/large.png)](https://codepen.io/Admin-GoCharting/full/angular-demo) #### TypeScript Integration Demo [![TypeScript Integration Demo Preview](https://codepen.io/Admin-GoCharting/pen/typescript-demo/image/large.png)](https://codepen.io/Admin-GoCharting/full/typescript-demo) #### Internationalization & Theme Demo [![Internationalization & Theme Demo Preview](https://codepen.io/Admin-GoCharting/pen/ZYWgxev/image/large.png)](https://codepen.io/Admin-GoCharting/full/ZYWgxev) --- ## Using These Examples ### 1. **Try Immediately** Click the embedded examples above to interact with live charts directly in your browser. ### 2. **Fork and Customize** - Click "Open in CodePen" - Fork the pen to your account - Modify the code to suit your needs - Use as a starting point for your project ### 3. **Copy Code** - View the HTML, CSS, and JavaScript tabs - Copy relevant sections to your project - Adapt the configuration for your use case ### 4. **Learn by Example** - Examine the data provider implementation - Study the chart configuration options - See how events and interactions work --- ## Related Documentation - **[Quick Start Guide](../tutorials/quick-start.md)** - Get started in 5 minutes - **[Vanilla JS Example](./vanilla-js.md)** - Complete vanilla JavaScript implementation - **[React Integration](./react-integration.md)** - React component examples - **[API Reference](../api/chart.md)** - Complete API documentation - **[Configuration Guide](../api/configuration.md)** - All configuration options --- ## Need Help? - **Documentation**: [gocharting.com/sdk/docs](https://gocharting.com/sdk/docs) - **Support**: admin@gocharting.com - **Discord**: [gocharting.com/discord](https://gocharting.com/discord) - **GitHub**: [github.com/gocharting/sdk](https://github.com/gocharting/sdk) --- _These examples are maintained and updated regularly. Last updated: September 2025_ File: docs/examples/react-example.md =============================================== # React Example This is a scaffold for the React integration example. Add code samples and integration steps for React here. File: docs/examples/react-integration.md =================================================== # React Integration Example Complete example of integrating GoCharting SDK with React applications. ## Demo Repository **Live Examples:** [https://github.com/akshay2796/gocharting-sdk-demo](https://github.com/akshay2796/gocharting-sdk-demo) - Complete React + TypeScript examples with MultiBasicChart, AdvancedTrading, and more. ## Prerequisites - React 18+ - Node.js 16+ - Basic knowledge of React hooks and components ## Installation ```bash # Create new React app (if needed) npx create-react-app my-trading-app cd my-trading-app # Install GoCharting SDK npm install @gocharting/chart-sdk # Install additional dependencies npm install axios luxon ``` ## Basic React Component Create a reusable chart component: ```jsx // components/TradingChart.jsx import React, { useEffect, useRef, useState } from "react"; import { ProfessionalChart, DataProvider } from "@gocharting/chart-sdk"; // Custom data provider for your API class ReactDataProvider extends DataProvider { constructor(apiBaseUrl, apiKey) { super(); this.apiBaseUrl = apiBaseUrl; this.apiKey = apiKey; } async _init(datacenter, token) { this.datacenter = datacenter; this.token = token; this.initialized = true; console.log("React DataProvider initialized"); } async getBars(symbolInfo, resolution, periodParams) { const { from, to } = periodParams; try { const response = await fetch(`${this.apiBaseUrl}/bars`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}`, }, body: JSON.stringify({ symbol: symbolInfo.ticker, resolution, from, to, }), }); const data = await response.json(); return { bars: data.bars.map((bar) => ({ time: bar.timestamp * 1000, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume, })), meta: { noData: data.bars.length === 0 }, }; } catch (error) { console.error("Failed to fetch bars:", error); return { bars: [], meta: { noData: true } }; } } async resolveSymbol(symbolName, onResolve, onError) { try { const response = await fetch( `${this.apiBaseUrl}/symbols/${symbolName}`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, }, ); const symbolData = await response.json(); const symbolInfo = { ...symbolData, // full GoCharting security from API ticker: symbolData.ticker || symbolData.symbol, full_name: symbolData.full_name || `${symbolData.exchange}:${symbolData.segment}:${symbolData.symbol}`, description: symbolData.description || symbolData.name, type: symbolData.type || (symbolData.asset_type || "EQUITY").toLowerCase(), }; onResolve(symbolInfo); } catch (error) { onError("Symbol not found"); } } async searchSymbols(userInput, callback) { try { const response = await fetch( `${this.apiBaseUrl}/search?q=${encodeURIComponent(userInput)}`, { headers: { Authorization: `Bearer ${this.apiKey}`, }, }, ); const results = await response.json(); callback(results); } catch (error) { callback([]); } } } const TradingChart = ({ symbol = "NASDAQ:AAPL", interval = "1D", theme = "dark", height = 600, apiConfig, licenseKey, onChartReady, onSymbolChange, onIntervalChange, }) => { const chartContainerRef = useRef(null); const chartRef = useRef(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { if (!chartContainerRef.current || !licenseKey) return; const initChart = async () => { try { setIsLoading(true); setError(null); // Create data provider const dataProvider = new ReactDataProvider( apiConfig.baseUrl, apiConfig.apiKey, ); // Create chart const chart = new ProfessionalChart({ container: chartContainerRef.current, symbol, interval, datafeed: dataProvider, licenseKey, config: { theme, height, trading: { enabled: true, showOrderLines: true, confirmOrders: true, }, ui: { showToolbar: true, showSidebar: true, showVolumePanel: true, showDrawingTools: true, }, performance: { maxCandles: 5000, animations: true, }, }, }); // Set up event handlers chart.onChartReady(() => { setIsLoading(false); onChartReady?.(chart); }); chart.onSymbolChange((newSymbol) => { onSymbolChange?.(newSymbol); }); chart.onIntervalChange((newInterval) => { onIntervalChange?.(newInterval); }); chartRef.current = chart; } catch (err) { setError(err.message); setIsLoading(false); } }; initChart(); // Cleanup return () => { if (chartRef.current) { chartRef.current.destroy(); chartRef.current = null; } }; }, [symbol, interval, theme, licenseKey, apiConfig]); // Update chart when props change useEffect(() => { if (chartRef.current && chartRef.current.isReady) { chartRef.current.setSymbol(symbol); } }, [symbol]); useEffect(() => { if (chartRef.current && chartRef.current.isReady) { chartRef.current.setInterval(interval); } }, [interval]); useEffect(() => { if (chartRef.current && chartRef.current.isReady) { chartRef.current.setTheme(theme); } }, [theme]); if (error) { return (

Chart Error

{error}

); } return (
{isLoading && (

Loading chart...

)}
); }; export default TradingChart; ``` ## App Component Use the chart component in your main app: ```jsx // App.jsx import React, { useState, useCallback } from "react"; import TradingChart from "./components/TradingChart"; import "./App.css"; const App = () => { const [currentSymbol, setCurrentSymbol] = useState("NASDAQ:AAPL"); const [currentInterval, setCurrentInterval] = useState("1D"); const [theme, setTheme] = useState("dark"); const [chartInstance, setChartInstance] = useState(null); // API configuration const apiConfig = { baseUrl: process.env.REACT_APP_API_BASE_URL || "https://api.yourbroker.com", apiKey: process.env.REACT_APP_API_KEY || "your-api-key", }; const licenseKey = process.env.REACT_APP_GOCHARTING_LICENSE || "demo-550e8400-e29b-41d4-a716-446655440000"; const handleChartReady = useCallback((chart) => { console.log("Chart is ready!"); setChartInstance(chart); // Set up broker data chart.setBrokerAccounts({ accountList: [ { id: "ACCOUNT_001", name: "Trading Account", balance: 50000, currency: "USD", }, ], orderBook: [], tradeBook: [], positions: [], }); }, []); const handleSymbolChange = useCallback((symbol) => { setCurrentSymbol(symbol); }, []); const handleIntervalChange = useCallback((interval) => { setCurrentInterval(interval); }, []); const handlePlaceOrder = async () => { if (!chartInstance) return; try { const order = await chartInstance.placeOrder({ symbol: currentSymbol, side: "buy", orderType: "market", size: 100, }); console.log("Order placed:", order); } catch (error) { console.error("Failed to place order:", error); } }; return (

My Trading Platform

); }; export default App; ``` ## Styling Add CSS for a professional look: ```css /* App.css */ .App { text-align: center; min-height: 100vh; background-color: #1a1a1a; color: white; } .app-header { background-color: #2d2d2d; padding: 20px; border-bottom: 1px solid #444; } .app-header h1 { margin: 0 0 20px 0; color: #00d4aa; } .controls { display: flex; gap: 15px; justify-content: center; align-items: center; flex-wrap: wrap; } .controls select, .controls button { padding: 8px 12px; border: 1px solid #555; background-color: #3d3d3d; color: white; border-radius: 4px; cursor: pointer; } .controls select:hover, .controls button:hover { background-color: #4d4d4d; } .controls button:disabled { opacity: 0.5; cursor: not-allowed; } .chart-container { padding: 20px; } .trading-chart { position: relative; border: 1px solid #444; border-radius: 8px; overflow: hidden; } .chart-loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1000; text-align: center; } .spinner { border: 4px solid #f3f3f3; border-top: 4px solid #00d4aa; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto 10px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .chart-error { padding: 40px; text-align: center; background-color: #2d2d2d; border: 1px solid #d32f2f; border-radius: 8px; color: #ff6b6b; } .chart-error h3 { margin-top: 0; color: #d32f2f; } .chart-error button { padding: 10px 20px; background-color: #d32f2f; color: white; border: none; border-radius: 4px; cursor: pointer; margin-top: 15px; } .chart-error button:hover { background-color: #b71c1c; } ``` ## Environment Variables Create a `.env` file for configuration: ```bash # .env REACT_APP_API_BASE_URL=https://api.yourbroker.com REACT_APP_API_KEY=your-api-key-here REACT_APP_GOCHARTING_LICENSE=your-license-key-here ``` ## Advanced Features ### Custom Hooks Create reusable hooks for chart management: ```jsx // hooks/useChart.js import { useState, useEffect, useRef } from "react"; import { ProfessionalChart } from "@gocharting/chart-sdk"; export const useChart = (config) => { const [chart, setChart] = useState(null); const [isReady, setIsReady] = useState(false); const [error, setError] = useState(null); const containerRef = useRef(null); useEffect(() => { if (!containerRef.current || !config.licenseKey) return; const initChart = async () => { try { const chartInstance = new ProfessionalChart({ container: containerRef.current, ...config, }); chartInstance.onChartReady(() => { setIsReady(true); setChart(chartInstance); }); chartInstance.onError((err) => { setError(err); }); } catch (err) { setError(err.message); } }; initChart(); return () => { if (chart) { chart.destroy(); } }; }, [config]); return { containerRef, chart, isReady, error, }; }; ``` ### Context Provider Create a context for sharing chart state: ```jsx // context/ChartContext.js import React, { createContext, useContext, useState } from "react"; const ChartContext = createContext(); export const ChartProvider = ({ children }) => { const [charts, setCharts] = useState({}); const [activeChart, setActiveChart] = useState(null); const addChart = (id, chart) => { setCharts((prev) => ({ ...prev, [id]: chart })); if (!activeChart) { setActiveChart(id); } }; const removeChart = (id) => { setCharts((prev) => { const newCharts = { ...prev }; delete newCharts[id]; return newCharts; }); if (activeChart === id) { setActiveChart(Object.keys(charts)[0] || null); } }; return ( {children} ); }; export const useChartContext = () => { const context = useContext(ChartContext); if (!context) { throw new Error("useChartContext must be used within ChartProvider"); } return context; }; ``` ## Production Build Build for production: ```bash # Build the app npm run build # Serve the build npm install -g serve serve -s build ``` ## Related Documentation - **[Configuration Guide](../guides/configuration.md)** - Chart configuration options - **[Trading Integration](../tutorials/trading-integration.md)** - Add trading features - **[API Reference](../api/)** - Complete API documentation --- _This example provides a solid foundation for React integration. Customize it based on your specific requirements!_ File: docs/examples/time-marks.md ============================================  > **Interactive lab:** open `examples/time-marks-lab.html` via `npm run serve` to try `getMarks` / `getTimescaleMarks` scenarios with the live demo WebSocket feed. # Time Marks Example This example demonstrates how to implement time marks (chart events) in the GoCharting SDK. Time marks appear as colored circles on the chart with hover tooltips and vertical lines. ## Features - **Colored circles** on the chart timeline - **Hover tooltips** with custom content - **Vertical dashed lines** on hover - **Multi-line tooltips** support - **Custom styling** options ## Basic Implementation ### 1. Datafeed with Time Marks ```javascript // Create a datafeed object with time marks support const myDatafeed = { async getTimeMarks(symbolInfo, from, to, resolution) { try { // Fetch time marks from your API const response = await fetch( `/api/timemarks?symbol=${symbolInfo.name}&from=${from}&to=${to}` ); const data = await response.json(); // Return in format return data.map((mark) => ({ id: mark.id, time: mark.timestamp, // Unix timestamp in seconds color: mark.color, // 'red', 'green', 'blue', etc. text: mark.description, // Tooltip content label: mark.type, // Single character (e.g., 'E', 'N', 'M') labelFontColor: "white", minSize: 25, })); } catch (error) { console.error("Failed to fetch time marks:", error); return []; } }, // ... other required methods like getBars, resolveSymbol }; ``` ### 2. Chart Integration ```javascript import { createChart } from "@gocharting/chart-sdk"; const chart = createChart("#chart-container", { datafeed: new MyDataProvider(), symbol: "BTCUSDT", interval: "1h", licenseKey: "YOUR_LICENSE_KEY", }); ``` ## Advanced Examples ### Multi-line Tooltips ```javascript async getTimeMarks(symbolInfo, from, to, resolution) { return [ { id: 1, time: 1757193488, color: 'red', text: [ 'Earnings Report Q3 2025', 'Expected: $1.45 EPS', 'Actual: $1.57 EPS' ], label: 'E', labelFontColor: 'white', minSize: 30 }, { id: 2, time: 1757539088, color: 'green', text: [ 'FDA Approval', 'Drug XYZ approved for market' ], label: 'N', labelFontColor: 'white', minSize: 25 } ]; } ``` ### Dynamic Color Coding ```javascript async getTimeMarks(symbolInfo, from, to, resolution) { const events = await this.fetchEvents(symbolInfo.name, from, to); return events.map(event => ({ id: event.id, time: event.timestamp, color: this.getEventColor(event.type), text: this.formatTooltip(event), label: event.type.charAt(0).toUpperCase(), labelFontColor: 'white', minSize: this.getEventSize(event.importance) })); } getEventColor(eventType) { const colors = { 'earnings': '#ff4444', 'news': '#4444ff', 'dividend': '#44ff44', 'split': '#ffaa44', 'merger': '#aa44ff' }; return colors[eventType] || '#888888'; } formatTooltip(event) { return [ event.title, `Date: ${new Date(event.timestamp * 1000).toLocaleDateString()}`, event.description ]; } getEventSize(importance) { return importance === 'high' ? 35 : importance === 'medium' ? 28 : 22; } ``` ## Configuration Options ### Time Mark Properties | Property | Type | Required | Description | | ---------------- | -------------- | -------- | ----------------------------------- | | `id` | string\|number | | Unique identifier | | `time` | number | | Unix timestamp in seconds | | `color` | string | | CSS color for the circle | | `text` | string\|array | | Tooltip content | | `label` | string | | Single character label | | `labelFontColor` | string | | Label text color (default: 'white') | | `minSize` | number | | Circle size in pixels (default: 25) | ### Supported Colors ```javascript // CSS color names color: "red"; color: "green"; color: "blue"; // Hex colors color: "#ff0000"; color: "#00ff00"; color: "#0000ff"; // RGB/RGBA color: "rgb(255, 0, 0)"; color: "rgba(255, 0, 0, 0.8)"; ``` ## User Interactions ### Hover Behavior - **Hover over mark** → Shows tooltip + vertical dashed line - **Move away** → Hides tooltip + vertical line - **Multiple marks** → Only hovered mark shows line ### Tooltip Styling The tooltip automatically styles with: - White background with subtle border - Colored left border matching the mark - Clean typography with proper spacing - Multi-line support for arrays ## Real-world Example ```javascript class CryptoDataProvider extends DataProvider { async getTimeMarks(symbolInfo, from, to, resolution) { // Fetch major crypto events const events = await this.fetchCryptoEvents(symbolInfo.name, from, to); return events.map((event) => { let color, label; switch (event.type) { case "halving": color = "#f39c12"; label = "H"; break; case "upgrade": color = "#3498db"; label = "U"; break; case "listing": color = "#2ecc71"; label = "L"; break; case "delisting": color = "#e74c3c"; label = "D"; break; default: color = "#95a5a6"; label = "N"; } return { id: event.id, time: event.timestamp, color, text: [ event.title, `Impact: ${event.impact}`, event.description, ], label, labelFontColor: "white", minSize: event.impact === "high" ? 32 : 26, }; }); } } ``` ## Troubleshooting ### Common Issues 1. **Marks not appearing** - Check that `getTimeMarks` returns valid data - Verify timestamps are in seconds (not milliseconds) - Ensure marks fall within the visible time range 2. **Tooltip not showing** - Verify `text` property is not empty - Check for JavaScript errors in console - Ensure mark has valid `id` property 3. **Vertical line not appearing** - Confirm hover detection is working - Check that mark has `markData` property - Verify canvas drawing context is not corrupted ### Debug Tips ```javascript // Add logging to debug time marks async getTimeMarks(symbolInfo, from, to, resolution) { const marks = await this.fetchTimeMarks(symbolInfo, from, to, resolution); console.log('Time marks loaded:', marks); return marks; } ``` ## Related Documentation - [DataProvider API](../api/datafeed.md) - [Chart Configuration](../api/configuration.md) - [Events API](../api/events.md) File: docs/examples/typescript-integration.md ======================================================== # TypeScript Integration Example Complete example of integrating GoCharting SDK with pure TypeScript using modern ES6+ features, classes, and interfaces. ## Live Demo & Repository **Demo Repository:** [https://github.com/akshay2796/gocharting-sdk-demo](https://github.com/akshay2796/gocharting-sdk-demo) - Complete TypeScript + React examples **CodePen TypeScript Demo:** [https://codepen.io/Admin-GoCharting/pen/typescript-demo](https://codepen.io/Admin-GoCharting/pen/typescript-demo) ## Prerequisites - TypeScript 5.0+ - Node.js 16+ - Basic knowledge of TypeScript and ES6+ features - Understanding of classes, interfaces, and generics ## Installation ```bash # Create new TypeScript project mkdir my-trading-app-ts cd my-trading-app-ts # Initialize package.json npm init -y # Install TypeScript and GoCharting SDK npm install typescript @types/node @gocharting/chart-sdk # Install development dependencies npm install -D ts-node nodemon @typescript-eslint/eslint-plugin # Create TypeScript config npx tsc --init ``` ## TypeScript Configuration ```json // tsconfig.json { "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "node", "lib": ["ES2020", "DOM", "DOM.Iterable"], "allowJs": true, "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, "declarationMap": true, "sourceMap": true, "removeComments": false, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "experimentalDecorators": true, "emitDecoratorMetadata": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` ## TypeScript Interfaces Define comprehensive type definitions: ```typescript // types/chart.types.ts export interface ISymbolInfo { // Basic Information exchange: string; segment: string; symbol: string; name: string; asset_type: "CRYPTO" | "EQUITY" | "FOREX" | "COMMODITY"; source_id: string; // Pair Information pair?: { from: string; to: string; }; // Trading Information tradeable: boolean; is_index: boolean; is_formula: boolean; // Data Feed Information delay_seconds: number; data_status: "streaming" | "endofday" | "pulsed" | "delayed_streaming"; data_source_location?: string; // Price & Volume Precision contract_size: number; tick_size: number; display_tick_size: number; volume_size_increment: number; max_tick_precision: number; max_volume_precision: number; quote_currency: string; // Futures-specific future_type?: string; // Exchange Information exchange_info: { name: string; code: string; country_cd: string; zone: string; has_unique_trade_id: boolean; logo_url: string; holidays: string[] | null; hours: Array<{ open: boolean }>; contains_ambiguous_symbols: boolean; valid_intervals: string[]; }; // Legacy/Compatibility Fields ticker?: string; full_name?: string; description?: string; type?: "stock" | "crypto" | "forex" | "commodity"; session?: string; timezone?: string; has_intraday?: boolean; has_daily?: boolean; supported_resolutions?: string[]; volume_precision?: number; tick_size?: number; // Minimum price movement } export interface IBarData { time: number; open: number; high: number; low: number; close: number; volume: number; } export interface IPeriodParams { from: number; to: number; firstDataRequest: boolean; } export interface IChartConfig { symbol: string; interval: string; theme: "light" | "dark"; height: number; container: HTMLElement; licenseKey: string; } export interface IChartStatus { isLoading: boolean; error: string | null; message: string; type: "info" | "success" | "error" | "warning"; } export interface IDataProvider { getBars( symbolInfo: ISymbolInfo, resolution: string, periodParams: IPeriodParams, ): Promise<{ bars: IBarData[]; meta: { noData: boolean } }>; resolveSymbol( symbolName: string, onResolve: (symbolInfo: ISymbolInfo) => void, onError: (error: string) => void, ): void; searchSymbols( userInput: string, callback: (symbols: ISymbolInfo[]) => void, ): void; } export type ChartEventType = | "ready" | "symbol_change" | "interval_change" | "error"; export interface IChartEventData { type: ChartEventType; data?: any; timestamp: number; } ``` ## TypeScript Classes Create a robust chart management system: ```typescript // services/ChartManager.ts import { ProfessionalChart } from "@gocharting/chart-sdk"; import { IChartConfig, IChartStatus, IDataProvider, ISymbolInfo, IBarData, IPeriodParams, IChartEventData, ChartEventType, } from "../types/chart.types"; export class ChartManager { private chart: ProfessionalChart | null = null; private config: IChartConfig; private status: IChartStatus; private eventListeners: Map< ChartEventType, Set<(data: IChartEventData) => void> > = new Map(); constructor(config: IChartConfig) { this.config = { ...config }; this.status = { isLoading: false, error: null, message: "Chart manager initialized", type: "info", }; // Initialize event listener maps ( [ "ready", "symbol_change", "interval_change", "error", ] as ChartEventType[] ).forEach((eventType) => { this.eventListeners.set(eventType, new Set()); }); } public async initialize(): Promise { try { this.updateStatus( true, null, "Initializing TypeScript chart...", "info", ); if (this.chart) { this.destroy(); } const dataProvider = new DemoDataProvider(); this.chart = new ProfessionalChart({ container: this.config.container, symbol: this.config.symbol, interval: this.config.interval, datafeed: dataProvider, licenseKey: this.config.licenseKey, config: { theme: { name: this.config.theme }, chart: { chartType: "candlestick", animations: true, grid: { horizontal: true, vertical: true }, }, ui: { showToolbar: true, showVolumePanel: true, showDrawingTools: true, }, }, }); this.setupEventHandlers(); this.updateStatus( false, null, "Chart initialized successfully", "success", ); this.emitEvent("ready", { chart: this.chart }); } catch (error: any) { const errorMessage = error.message || "Unknown initialization error"; this.updateStatus( false, errorMessage, `Initialization failed: ${errorMessage}`, "error", ); this.emitEvent("error", { error: errorMessage }); throw error; } } public async changeSymbol(symbol: string): Promise { if (!this.chart) { throw new Error("Chart not initialized"); } try { this.updateStatus(true, null, `Loading symbol: ${symbol}`, "info"); await this.chart.setSymbol(symbol); this.config.symbol = symbol; this.updateStatus( false, null, `Symbol changed to: ${symbol}`, "success", ); this.emitEvent("symbol_change", { symbol }); } catch (error: any) { const errorMessage = `Failed to change symbol: ${error.message}`; this.updateStatus(false, errorMessage, errorMessage, "error"); throw error; } } public async changeInterval(interval: string): Promise { if (!this.chart) { throw new Error("Chart not initialized"); } try { this.updateStatus( true, null, `Changing interval to: ${interval}`, "info", ); await this.chart.setInterval(interval); this.config.interval = interval; this.updateStatus( false, null, `Interval changed to: ${interval}`, "success", ); this.emitEvent("interval_change", { interval }); } catch (error: any) { const errorMessage = `Failed to change interval: ${error.message}`; this.updateStatus(false, errorMessage, errorMessage, "error"); throw error; } } public changeTheme(theme: "light" | "dark"): void { if (!this.chart) { throw new Error("Chart not initialized"); } this.chart.setTheme(theme); this.config.theme = theme; this.updateStatus(false, null, `Theme changed to: ${theme}`, "info"); } public addEventListener( eventType: ChartEventType, callback: (data: IChartEventData) => void, ): void { const listeners = this.eventListeners.get(eventType); if (listeners) { listeners.add(callback); } } public removeEventListener( eventType: ChartEventType, callback: (data: IChartEventData) => void, ): void { const listeners = this.eventListeners.get(eventType); if (listeners) { listeners.delete(callback); } } public getStatus(): IChartStatus { return { ...this.status }; } public getConfig(): IChartConfig { return { ...this.config }; } public destroy(): void { if (this.chart) { this.chart.destroy(); this.chart = null; } // Clear all event listeners this.eventListeners.forEach((listeners) => listeners.clear()); this.updateStatus(false, null, "Chart destroyed", "info"); } private setupEventHandlers(): void { if (!this.chart) return; this.chart.onChartReady(() => { this.updateStatus( false, null, `Chart ready: ${this.config.symbol}`, "success", ); }); this.chart.onSymbolChange((symbolInfo: any) => { this.config.symbol = symbolInfo.name; this.updateStatus( false, null, `Symbol: ${symbolInfo.description}`, "info", ); }); this.chart.onIntervalChange((interval: string) => { this.config.interval = interval; this.updateStatus(false, null, `Timeframe: ${interval}`, "info"); }); this.chart.onError((error: any) => { const errorMessage = error.message || "Chart error occurred"; this.updateStatus( false, errorMessage, `Chart error: ${errorMessage}`, "error", ); this.emitEvent("error", { error: errorMessage }); }); } private updateStatus( isLoading: boolean, error: string | null, message: string, type: IChartStatus["type"], ): void { this.status = { isLoading, error, message, type }; } private emitEvent(eventType: ChartEventType, data?: any): void { const listeners = this.eventListeners.get(eventType); if (listeners) { const eventData: IChartEventData = { type: eventType, data, timestamp: Date.now(), }; listeners.forEach((callback) => { try { callback(eventData); } catch (error) { console.error( `Error in event listener for ${eventType}:`, error, ); } }); } } } ``` ## Data Provider Implementation ```typescript // services/DemoDataProvider.ts import { IDataProvider, ISymbolInfo, IBarData, IPeriodParams, } from "../types/chart.types"; export class DemoDataProvider implements IDataProvider { private readonly symbolDatabase: Map> = new Map([ ["AAPL", { description: "Apple Inc.", type: "stock" }], ["MSFT", { description: "Microsoft Corporation", type: "stock" }], ["GOOGL", { description: "Alphabet Inc.", type: "stock" }], ["BTCUSDT", { description: "Bitcoin / Tether", type: "crypto" }], ["EURUSD", { description: "Euro / US Dollar", type: "forex" }], ]); public async getBars( symbolInfo: ISymbolInfo, resolution: string, periodParams: IPeriodParams, ): Promise<{ bars: IBarData[]; meta: { noData: boolean } }> { console.log( ` TypeScript: Fetching bars for ${symbolInfo.name}, resolution: ${resolution}`, ); const bars = this.generateDemoData( symbolInfo, periodParams.from, periodParams.to, resolution, ); return { bars, meta: { noData: bars.length === 0 }, }; } public resolveSymbol( symbolName: string, onResolve: (symbolInfo: ISymbolInfo) => void, onError: (error: string) => void, ): void { console.log(` TypeScript: Resolving symbol: ${symbolName}`); try { const [exchange, segment, ticker] = symbolName.includes(":") ? symbolName.split(":") : ["DEMO", "SPOT", symbolName]; const symbolData = this.symbolDatabase.get(ticker); const symbolInfo: ISymbolInfo = { // Basic Information exchange: exchange, segment: segment, symbol: ticker, name: symbolData?.description || `${ticker} Demo`, asset_type: symbolData?.type === "crypto" ? "CRYPTO" : "EQUITY", source_id: ticker, // Trading Information tradeable: true, is_index: false, is_formula: false, // Data Feed Information delay_seconds: 0, data_status: "streaming", // Price & Volume Precision contract_size: 1, tick_size: 0.01, display_tick_size: 0.01, volume_size_increment: 0.001, max_tick_precision: 2, max_volume_precision: 3, quote_currency: "USD", // Exchange Information exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "UTC", has_unique_trade_id: true, logo_url: "", holidays: null, hours: Array(7).fill({ open: true }), contains_ambiguous_symbols: false, valid_intervals: [ "1m", "5m", "15m", "30m", "1h", "4h", "1D", "1W", "1M", ], }, // Legacy/Compatibility Fields ticker: ticker, full_name: `${exchange}:${segment}:${ticker}`, description: symbolData?.description || `${ticker} Demo`, type: symbolData?.type || "stock", session: "24x7", timezone: "Etc/UTC", has_intraday: true, has_daily: true, supported_resolutions: [ "1", "5", "15", "30", "60", "240", "1D", "1W", "1M", ], volume_precision: 3, }; setTimeout(() => onResolve(symbolInfo), 100); } catch (error: any) { onError(`Symbol resolution failed: ${error.message}`); } } public searchSymbols( userInput: string, callback: (symbols: ISymbolInfo[]) => void, ): void { const symbols: ISymbolInfo[] = []; this.symbolDatabase.forEach((data, ticker) => { if ( ticker.toLowerCase().includes(userInput.toLowerCase()) || data.description ?.toLowerCase() .includes(userInput.toLowerCase()) ) { const exchange = data.type === "crypto" ? "BINANCE" : data.type === "forex" ? "FX" : "NASDAQ"; symbols.push({ name: `${exchange}:${ticker}`, full_name: `${exchange}:${ticker}`, description: data.description || `${ticker} Demo`, type: data.type || "stock", session: "24x7", timezone: "Etc/UTC", 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, }); } }); callback(symbols); } private generateDemoData( symbolInfo: ISymbolInfo, from: number, to: number, resolution: string, ): IBarData[] { const bars: IBarData[] = []; const interval = this.getIntervalMs(resolution); const startTime = from * 1000; const endTime = to * 1000; let currentTime = startTime; let price = this.getBasePrice(symbolInfo.ticker); while (currentTime <= endTime && bars.length < 1000) { const volatility = this.getVolatility(symbolInfo.type); const change = (Math.random() - 0.5) * price * volatility; const open = price; const close = Math.max(0.01, price + change); const high = Math.max(open, close) * (1 + Math.random() * 0.01); const low = Math.min(open, close) * (1 - Math.random() * 0.01); const volume = this.generateVolume(symbolInfo.type); bars.push({ time: currentTime, open: this.roundPrice(open), high: this.roundPrice(high), low: this.roundPrice(Math.max(0.01, low)), close: this.roundPrice(close), volume, }); price = close; currentTime += interval; } return bars; } private getIntervalMs(resolution: string): number { const intervals: Record = { "1": 60 * 1000, "5": 5 * 60 * 1000, "15": 15 * 60 * 1000, "30": 30 * 60 * 1000, "60": 60 * 60 * 1000, "240": 4 * 60 * 60 * 1000, "1D": 24 * 60 * 60 * 1000, "1W": 7 * 24 * 60 * 60 * 1000, "1M": 30 * 24 * 60 * 60 * 1000, }; return intervals[resolution] || intervals["1D"]; } private getBasePrice(ticker: string): number { const basePrices: Record = { AAPL: 150, MSFT: 300, GOOGL: 2500, BTCUSDT: 45000, EURUSD: 1.1, }; return basePrices[ticker] || 100; } private getVolatility(type: ISymbolInfo["type"]): number { const volatilities: Record = { stock: 0.02, crypto: 0.05, forex: 0.01, commodity: 0.03, }; return volatilities[type] || 0.02; } private generateVolume(type: ISymbolInfo["type"]): number { const baseVolumes: Record = { stock: 1000000, crypto: 500000, forex: 10000000, commodity: 750000, }; const base = baseVolumes[type] || 1000000; return Math.floor(Math.random() * base) + base * 0.1; } private roundPrice(price: number): number { return Math.round(price * 100) / 100; } } ``` ## Chart Controller ```typescript // controllers/ChartController.ts import { ChartManager } from "../services/ChartManager"; import { IChartConfig, IChartEventData, ChartEventType, } from "../types/chart.types"; export class ChartController { private chartManager: ChartManager; private container: HTMLElement; private controlsContainer: HTMLElement; constructor( containerId: string, controlsId: string, config: Omit, ) { const container = document.getElementById(containerId); const controlsContainer = document.getElementById(controlsId); if (!container) { throw new Error( `Container element with id '${containerId}' not found`, ); } if (!controlsContainer) { throw new Error( `Controls container element with id '${controlsId}' not found`, ); } this.container = container; this.controlsContainer = controlsContainer; this.chartManager = new ChartManager({ ...config, container: this.container, }); this.setupEventListeners(); this.createControls(); } public async initialize(): Promise { await this.chartManager.initialize(); } public destroy(): void { this.chartManager.destroy(); } private setupEventListeners(): void { this.chartManager.addEventListener("ready", (data: IChartEventData) => { console.log("Chart ready event:", data); this.updateStatus("Chart is ready!", "success"); }); this.chartManager.addEventListener( "symbol_change", (data: IChartEventData) => { console.log("Symbol change event:", data); this.updateStatus( `Symbol changed to: ${data.data?.symbol}`, "info", ); }, ); this.chartManager.addEventListener( "interval_change", (data: IChartEventData) => { console.log("Interval change event:", data); this.updateStatus( `Interval changed to: ${data.data?.interval}`, "info", ); }, ); this.chartManager.addEventListener("error", (data: IChartEventData) => { console.error("Chart error event:", data); this.updateStatus(`Error: ${data.data?.error}`, "error"); }); } private createControls(): void { this.controlsContainer.innerHTML = `
Ready to initialize chart
`; this.attachControlEvents(); } private attachControlEvents(): void { const symbolSelect = document.getElementById( "symbol-select", ) as HTMLSelectElement; const intervalSelect = document.getElementById( "interval-select", ) as HTMLSelectElement; const themeSelect = document.getElementById( "theme-select", ) as HTMLSelectElement; symbolSelect?.addEventListener("change", async (event) => { const target = event.target as HTMLSelectElement; try { await this.chartManager.changeSymbol(target.value); } catch (error: any) { this.updateStatus( `Failed to change symbol: ${error.message}`, "error", ); } }); intervalSelect?.addEventListener("change", async (event) => { const target = event.target as HTMLSelectElement; try { await this.chartManager.changeInterval(target.value); } catch (error: any) { this.updateStatus( `Failed to change interval: ${error.message}`, "error", ); } }); themeSelect?.addEventListener("change", (event) => { const target = event.target as HTMLSelectElement; try { this.chartManager.changeTheme(target.value as "light" | "dark"); } catch (error: any) { this.updateStatus( `Failed to change theme: ${error.message}`, "error", ); } }); } private updateStatus( message: string, type: "info" | "success" | "error", ): void { const statusDisplay = document.getElementById("status-display"); if (statusDisplay) { statusDisplay.textContent = message; statusDisplay.className = `status-display ${type}`; } } } ``` ## Usage Example ```typescript // main.ts import { ChartController } from "./controllers/ChartController"; import "./styles/chart.css"; class TradingApp { private chartController: ChartController | null = null; constructor() { this.initializeApp(); } private async initializeApp(): Promise { try { // Wait for DOM to be ready if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => this.setupChart(), ); } else { this.setupChart(); } } catch (error: any) { console.error("Failed to initialize app:", error); } } private async setupChart(): Promise { try { this.chartController = new ChartController( "chart-container", "chart-controls", { symbol: "NASDAQ:AAPL", interval: "1D", theme: "dark", height: 500, licenseKey: "your-license-key-here", }, ); await this.chartController.initialize(); console.log( " TypeScript chart application initialized successfully", ); } catch (error: any) { console.error(" Failed to setup chart:", error); } } public destroy(): void { if (this.chartController) { this.chartController.destroy(); this.chartController = null; } } } // Initialize the application const app = new TradingApp(); // Cleanup on page unload window.addEventListener("beforeunload", () => { app.destroy(); }); // Export for global access (window as any).TradingApp = app; ``` ## CSS Styles ```css /* styles/chart.css */ .chart-controls { display: flex; gap: 1rem; padding: 1rem; background: #f8f9fa; border-radius: 8px; margin-bottom: 1rem; flex-wrap: wrap; align-items: end; } .control-group { display: flex; flex-direction: column; gap: 0.5rem; min-width: 120px; } .control-group label { font-weight: 600; font-size: 0.9rem; color: #495057; } .control-group select { padding: 0.5rem; border: 1px solid #ced4da; border-radius: 4px; background: white; font-size: 0.9rem; cursor: pointer; transition: border-color 0.3s ease; } .control-group select:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); } .status-display { padding: 0.75rem; border-radius: 4px; font-size: 0.9rem; font-weight: 500; min-width: 200px; text-align: center; transition: all 0.3s ease; } .status-display.info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; } .status-display.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; } .status-display.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } #chart-container { width: 100%; height: 500px; border: 1px solid #e1e5e9; border-radius: 8px; background: white; } /* Responsive design */ @media (max-width: 768px) { .chart-controls { flex-direction: column; gap: 0.75rem; } .control-group { width: 100%; min-width: unset; } #chart-container { height: 400px; } } ``` ## HTML Structure ```html TypeScript Trading Chart

TypeScript Trading Chart Demo

``` ## Advanced Features ### Generic Chart Factory ```typescript // factories/ChartFactory.ts import { ChartManager } from "../services/ChartManager"; import { IChartConfig } from "../types/chart.types"; export class ChartFactory { private static instances: Map = new Map(); public static async createChart>( id: string, config: T & Pick, ): Promise { if (this.instances.has(id)) { throw new Error(`Chart with id '${id}' already exists`); } const defaultConfig: IChartConfig = { symbol: "NASDAQ:AAPL", interval: "1D", theme: "dark", height: 500, ...config, }; const chartManager = new ChartManager(defaultConfig); await chartManager.initialize(); this.instances.set(id, chartManager); return chartManager; } public static getChart(id: string): ChartManager | undefined { return this.instances.get(id); } public static destroyChart(id: string): boolean { const chart = this.instances.get(id); if (chart) { chart.destroy(); this.instances.delete(id); return true; } return false; } public static destroyAllCharts(): void { this.instances.forEach((chart, id) => { chart.destroy(); }); this.instances.clear(); } } ``` ### Decorator Pattern ```typescript // decorators/ChartDecorators.ts // Method decorator for logging export function LogMethod( target: any, propertyName: string, descriptor: PropertyDescriptor, ) { const method = descriptor.value; descriptor.value = function (...args: any[]) { console.log( ` Calling ${target.constructor.name}.${propertyName} with args:`, args, ); const result = method.apply(this, args); console.log(` ${target.constructor.name}.${propertyName} completed`); return result; }; } // Class decorator for performance monitoring export function PerformanceMonitor( constructor: T, ) { return class extends constructor { constructor(...args: any[]) { const start = performance.now(); super(...args); const end = performance.now(); console.log( ` ${constructor.name} initialization took ${end - start}ms`, ); } }; } // Usage example @PerformanceMonitor export class MonitoredChartManager extends ChartManager { @LogMethod public async changeSymbol(symbol: string): Promise { return super.changeSymbol(symbol); } @LogMethod public async changeInterval(interval: string): Promise { return super.changeInterval(interval); } } ``` ## Related Documentation - **[CodePen Live Demo](https://codepen.io/Admin-GoCharting/pen/typescript-demo)** - Try the TypeScript example - **[Quick Start Guide](../tutorials/quick-start.md)** - Get started with the SDK - **[Angular Integration](./angular-integration.md)** - Angular component examples - **[Vue.js Integration](./vue-integration.md)** - Vue.js component examples - **[API Reference](../api/chart.md)** - Complete API documentation - **[Configuration Guide](../api/configuration.md)** - All configuration options ## Troubleshooting ### Common Issues **TypeScript compilation errors:** - Ensure TypeScript version is 5.0+ - Check that all type definitions are properly imported - Verify tsconfig.json configuration is correct - Install missing type packages: `npm install @types/node` **Chart not rendering:** - Verify the container element exists before initialization - Check that the license key is valid - Ensure proper async/await usage in initialization - Verify DOM is ready before creating chart **Interface/Type errors:** - Make sure all interfaces are properly exported/imported - Check for circular dependencies in type definitions - Ensure generic types are properly constrained - Verify interface implementations are complete **Memory leaks:** - Always call destroy() methods in cleanup - Remove event listeners properly - Clear Map/Set collections when done - Use WeakMap/WeakSet for temporary references **Module resolution issues:** - Check moduleResolution in tsconfig.json - Verify import paths are correct - Ensure proper file extensions in imports - Check that all dependencies are installed --- _Ready to use TypeScript with GoCharting SDK in production? Get your license key at [gocharting.com](https://gocharting.com)!_ File: docs/examples/typescript.md ============================================ # TypeScript Example Complete TypeScript + React implementation example for GoCharting SDK with full type safety and modern React patterns. ## Live Demo & Repository **Live Demo Repository:** [https://github.com/akshay2796/gocharting-sdk-demo](https://github.com/akshay2796/gocharting-sdk-demo) **CodePen TypeScript Demo:** [https://codepen.io/Admin-GoCharting/pen/typescript-demo](https://codepen.io/Admin-GoCharting/pen/typescript-demo) ## Quick Setup ### 1. Clone the Demo Repository ```bash # Clone the complete TypeScript/React demo git clone https://github.com/akshay2796/gocharting-sdk-demo.git cd gocharting-sdk-demo # Install dependencies npm install # Start development server npm start ``` ### 2. Or Create New React + TypeScript App ```bash # Create new React app with TypeScript npx create-react-app my-chart-app --template typescript cd my-chart-app # Install GoCharting SDK npm install @gocharting/chart-sdk # Start development npm start ``` ## Complete TypeScript + React Implementation ### MultiBasicChart Component This is a real-world example from the [demo repository](https://github.com/akshay2796/gocharting-sdk-demo) showing proper TypeScript types, React hooks, and chart lifecycle management. ```typescript // components/MultiBasicChart.tsx import { useEffect, useRef, useState } from "react"; import * as GoChartingSDK from "@gocharting/chart-sdk"; import type { ChartInstance, ChartWrapper } from "@gocharting/chart-sdk"; import { createChartDatafeed } from "../utils/chart-datafeed"; import "./MultiBasicChart.css"; const MultiBasicChart = () => { const chartContainerRef = useRef(null); // ChartWrapper is the wrapper returned by createChart(), ChartInstance is the component const chartWrapperRef = useRef(null); const chartInstanceRef = useRef(null); const [status, setStatus] = useState("Initializing chart..."); const [currentSymbol, setCurrentSymbol] = useState( "BYBIT:FUTURE:BTCUSDT" ); useEffect(() => { let mounted = true; const initializeChart = async () => { if (!chartContainerRef.current) return; try { setStatus("Creating chart..."); // Create datafeed const datafeed = createChartDatafeed(); // Create chart using SDK const chart = GoChartingSDK.createChart( chartContainerRef.current, { symbol: currentSymbol, interval: "1m", datafeed: datafeed, debugLog: false, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", exclude: { indicators: [ "ACC", "CHAIKINMFI", "CHAIKINVOLATILITY", "COPPOCK", "EOM", "FORCEINDEX", "KLINGER", "KST", "MFI", "ONBALANCEVOLUME", "ROC", "SMA", "TWIGGSMONEYFLOW", "VOLUMEUNDERLAY", "VWAP", "VWMA", "WMFI", "OI", "SANBAND", "TRADEVOLUMEINDEX", "VOLUMEOSCILLATOR", "VOLUMEROC", ], }, theme: "dark", onReady: (chartInstance: any) => { // Store the actual chart instance from the callback chartInstanceRef.current = chartInstance; if (mounted) { setStatus("Chart ready!"); } }, onError: (error: Error) => { console.error("Chart creation error:", error); if (mounted) { setStatus(` Error: ${error.message}`); } }, } ); // Store the chart wrapper (has destroy() method) if (!chartWrapperRef.current) { chartWrapperRef.current = chart; } } catch (error) { console.error(" Error initializing chart:", error); if (mounted) { setStatus( ` Error: ${ error instanceof Error ? error.message : "Unknown error" }` ); } } }; initializeChart(); return () => { mounted = false; if ( chartWrapperRef.current && !chartWrapperRef.current.isDestroyed() ) { try { chartWrapperRef.current.destroy(); } catch (error) { console.error("Error destroying chart:", error); } } chartInstanceRef.current = null; chartWrapperRef.current = null; }; }, []); const handleSymbolChange = (newSymbol: string) => { if (!chartInstanceRef.current) { setStatus(" Chart not ready"); return; } try { setStatus(` Switching to ${newSymbol}...`); chartInstanceRef.current.setSymbol(newSymbol); setCurrentSymbol(newSymbol); setStatus(` Switched to ${newSymbol}`); } catch (error) { console.error(" Error changing symbol:", error); setStatus( ` Error: ${ error instanceof Error ? error.message : "Unknown error" }` ); } }; const handleResubscribeAll = () => { if (!chartInstanceRef.current) { setStatus(" Chart not ready"); return; } try { chartInstanceRef.current.resubscribeAll(); setStatus(" Resubscribed to all data streams"); } catch (error) { console.error(" Error resubscribing:", error); setStatus( ` Error: ${ error instanceof Error ? error.message : "Unknown error" }` ); } }; return (

GoCharting SDK Demo

Professional Financial Charts with Built-in AutoFit

Loading chart...
{status}
); }; export default MultiBasicChart; ``` ## � CSS Styling ```css /* MultiBasicChart.css */ .container { max-width: 1400px; margin: 0 auto; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } .header { text-align: center; margin-bottom: 30px; } .header h1 { color: #4a90e2; margin-bottom: 10px; } .header p { color: #888; font-size: 14px; } .controls { display: flex; gap: 10px; justify-content: center; margin: 20px 0; flex-wrap: wrap; } .btn { padding: 12px 24px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 600; transition: all 0.2s; } .btn.primary { background: #4a90e2; color: white; } .btn.primary:hover { background: #357abd; transform: translateY(-2px); } .btn.success { background: #28a745; color: white; } .btn.success:hover { background: #218838; transform: translateY(-2px); } #chart-container { width: 100%; height: 600px; border: 1px solid #333; border-radius: 8px; margin: 20px 0; position: relative; } .loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #888; font-size: 16px; } .status { text-align: center; padding: 10px; background: #2a2a2a; border-radius: 6px; color: #4a90e2; font-size: 14px; margin-top: 20px; } ``` ## Key TypeScript Features ### 1. **Proper Type Imports** ```typescript // Import SDK types for full type safety import * as GoChartingSDK from "@gocharting/chart-sdk"; import type { ChartInstance, ChartWrapper } from "@gocharting/chart-sdk"; ``` **Important Type Distinction:** - `ChartWrapper` - Returned by `createChart()`, has `destroy()` and `isDestroyed()` methods - `ChartInstance` - The actual chart component passed to `onReady` callback, has methods like `setSymbol()`, `resubscribeAll()` ### 2. **React Hooks for Chart Lifecycle** ```typescript const chartWrapperRef = useRef(null); const chartInstanceRef = useRef(null); useEffect(() => { let mounted = true; const initializeChart = async () => { // Chart initialization const chart = GoChartingSDK.createChart(container, config); chartWrapperRef.current = chart; }; initializeChart(); return () => { mounted = false; if (chartWrapperRef.current && !chartWrapperRef.current.isDestroyed()) { chartWrapperRef.current.destroy(); } }; }, []); ``` ### 3. **Type-Safe Configuration** ```typescript const config = { symbol: currentSymbol, interval: "1m", datafeed: datafeed, debugLog: false, licenseKey: "demo-550e8400-e29b-41d4-a716-446655440000", exclude: { indicators: ["SMA", "EMA", "RSI"], // Type-safe indicator exclusion }, theme: "dark" as const, // Literal type for theme onReady: (chartInstance: ChartInstance) => { chartInstanceRef.current = chartInstance; }, onError: (error: Error) => { console.error("Chart error:", error); }, }; ``` ## Project Configuration ### Package.json (from demo repository) ```json { "name": "gocharting-sdk-demo-cra", "version": "0.1.0", "private": true, "repository": { "type": "git", "url": "https://github.com/akshay2796/gocharting-sdk-demo.git" }, "dependencies": { "@gocharting/chart-sdk": "1.0.35", "@types/node": "^25.0.8", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router-dom": "^7.12.0", "react-scripts": "5.0.1", "typescript": "^5.2.2" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test" } } ``` ### TypeScript Configuration ```json // tsconfig.json { "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx" }, "include": ["src"] } ``` ## Key Benefits of TypeScript Integration ### 1. **Full Type Safety** - Compile-time error detection - IntelliSense and autocomplete in VS Code - Refactoring confidence with type checking - Proper types for `ChartWrapper` and `ChartInstance` ### 2. **React Best Practices** - Proper cleanup in `useEffect` return - Mounted flag to prevent state updates after unmount - Type-safe refs with `useRef(null)` - Error handling with TypeScript error types ### 3. **Better Developer Experience** - IDE support and debugging - Code documentation through types - Easier maintenance and collaboration - Real-world examples from production code ## Related Documentation - **[Demo Repository](https://github.com/akshay2796/gocharting-sdk-demo)** - Complete working examples - **[TypeScript Integration Guide](./typescript-integration.md)** - Comprehensive TypeScript implementation - **[Type Definitions](../api/types.md)** - Complete TypeScript types - **[React Integration](./react-integration.md)** - React-specific patterns - **[Quick Start Guide](../tutorials/quick-start.md)** - Get started with the SDK - **[API Reference](../api/chart.md)** - Complete API documentation ## Common Issues & Solutions ### TypeScript Compilation Errors **Problem:** Type errors when importing SDK ```typescript // Wrong import GoChartingSDK from "@gocharting/chart-sdk"; // Correct import * as GoChartingSDK from "@gocharting/chart-sdk"; import type { ChartInstance, ChartWrapper } from "@gocharting/chart-sdk"; ``` **Problem:** Missing type definitions ```bash # Solution: Ensure SDK version 1.0.35+ which includes full TypeScript types npm install @gocharting/chart-sdk@latest ``` ### Chart Not Rendering **Problem:** Chart container not found ```typescript // Always check if ref exists before creating chart if (!chartContainerRef.current) return; ``` **Problem:** Memory leaks in React ```typescript // Always cleanup in useEffect return return () => { if (chartWrapperRef.current && !chartWrapperRef.current.isDestroyed()) { chartWrapperRef.current.destroy(); } }; ``` ### Symbol Change Not Working **Problem:** Using wrong ref ```typescript // Wrong - chartWrapperRef doesn't have setSymbol chartWrapperRef.current.setSymbol(symbol); // Correct - use chartInstanceRef from onReady callback chartInstanceRef.current.setSymbol(symbol); ``` ## Next Steps 1. **Clone the demo repository:** ```bash git clone https://github.com/akshay2796/gocharting-sdk-demo.git cd gocharting-sdk-demo npm install && npm start ``` 2. **Explore the examples:** - `src/components/MultiBasicChart.tsx` - Basic chart with symbol switching - `src/components/ChartSDKAdvanced.tsx` - Advanced trading features - `src/pages/` - Complete page implementations 3. **Read the comprehensive guide:** - [TypeScript Integration Guide](./typescript-integration.md) for advanced patterns --- _This example is from the [official demo repository](https://github.com/akshay2796/gocharting-sdk-demo) and represents production-ready code._ File: docs/examples/vanilla-js-example.md ==================================================== # Vanilla JS Example This is a scaffold for the Vanilla JS integration example. Add code samples and integration steps for Vanilla JS here. File: docs/examples/vanilla-js.md ============================================ # Vanilla JavaScript Example Complete vanilla JavaScript implementation that can be run directly in CodePen, CodeSandbox, or any web environment. ## Live Demo **CodePen Basic:** [https://codepen.io/Admin-GoCharting/pen/GgpVepq](https://codepen.io/Admin-GoCharting/pen/GgpVepq) **CodePen Advanced:** [https://codepen.io/Admin-GoCharting/pen/xbwvBbe](https://codepen.io/Admin-GoCharting/pen/xbwvBbe) **CodeSandbox:** [https://codesandbox.io/s/gocharting-vanilla-js](https://codesandbox.io/s/gocharting-vanilla-js) ## Complete Implementation ### HTML Structure ```html GoCharting SDK - Vanilla JS Demo

GoCharting SDK Demo

Professional Financial Charting in Vanilla JavaScript

Loading professional chart...
Initializing GoCharting SDK...

Professional Charts

Advanced candlestick, line, and area charts with professional styling and smooth animations.

Technical Indicators

50+ built-in technical indicators including RSI, MACD, Bollinger Bands, and more.

Drawing Tools

Comprehensive drawing tools for technical analysis including trendlines, Fibonacci, and shapes.

Real-time Data

Live market data updates with WebSocket support for real-time price streaming.

``` ## Key Features Demonstrated ### 1. **Professional Chart Display** - Candlestick, line, area, and OHLC bar charts - Smooth animations and professional styling - Responsive design for mobile and desktop ### 2. **Interactive Controls** - Symbol selection with popular stocks and crypto - Timeframe switching (1m to 1W) - Chart type switching - Theme switching (dark/light) - Custom symbol input ### 3. **Demo Data Generation** - Realistic price movements with volatility - Volume data simulation - Multiple asset types (stocks, crypto, forex) ### 4. **Error Handling** - Graceful error handling and user feedback - Loading states and status updates - Fallback for failed operations ## Customization Options ### Modify Chart Configuration ```javascript const customConfig = { theme: { name: "dark", colors: { background: "#1a1a1a", upColor: "#00d4aa", downColor: "#ff6b6b", }, }, chart: { chartType: "candlestick", animations: true, maxCandles: 5000, }, ui: { showToolbar: true, showVolumePanel: true, showDrawingTools: true, showLegend: true, }, }; ``` ### Add Custom Indicators ```javascript // Add RSI indicator chart.addIndicator("RSI", { period: 14, overbought: 70, oversold: 30, }); // Add Moving Average chart.addIndicator("SMA", { period: 20, source: "close", }); ``` ## Mobile Responsive The example includes responsive design that works on: - **Desktop** - Full feature set with all controls - **Tablet** - Optimized layout with touch support - **Mobile** - Compact layout with essential controls ## Integration with Your App To integrate this into your application: 1. **Replace Demo Data Provider** with your real data source 2. **Add Authentication** for your API endpoints 3. **Customize Styling** to match your brand 4. **Add Trading Features** if needed (requires Professional license) ## Next Steps - **[React Integration](./react-integration.md)** - React implementation - **[Vue.js Integration](./vue-integration.md)** - Vue.js implementation - **[TypeScript Example](./typescript.md)** - TypeScript implementation - **[Trading Integration](../tutorials/trading-integration.md)** - Add trading features --- _Ready to use this in production? Get your license key at [gocharting.com](https://gocharting.com)!_ File: docs/examples/vue-example.md ============================================= # Vue Example This is a scaffold for the Vue integration example. Add code samples and integration steps for Vue here. File: docs/examples/vue-integration.md ================================================= # Vue.js Integration Example Complete example of integrating GoCharting SDK with Vue.js applications using the Composition API. ## Live Demo **CodePen Vue.js Demo:** [https://codepen.io/Admin-GoCharting/pen/vue-demo](https://codepen.io/Admin-GoCharting/pen/vue-demo) ## Prerequisites - Vue 3.0+ - Node.js 16+ - Basic knowledge of Vue Composition API ## Installation ```bash # Create new Vue app (if needed) npm create vue@latest my-trading-app cd my-trading-app # Install GoCharting SDK npm install @gocharting/chart-sdk # Install additional dependencies npm install axios ``` ## Basic Vue Component Create a reusable chart component using Vue 3 Composition API: ```vue ``` ## Usage in Parent Component ```vue ``` ## Advanced Features ### Composition API with Custom Hook Create a reusable composable for chart management: ```javascript // composables/useChart.js import { ref, onUnmounted } from "vue"; import { ProfessionalChart } from "@gocharting/chart-sdk"; export function useChart() { const chart = ref(null); const isLoading = ref(false); const error = ref(null); const createChart = async (container, options) => { try { isLoading.value = true; error.value = null; if (chart.value) { chart.value.destroy(); } chart.value = new ProfessionalChart({ container, ...options, }); return chart.value; } catch (err) { error.value = err.message; throw err; } finally { isLoading.value = false; } }; const destroyChart = () => { if (chart.value) { chart.value.destroy(); chart.value = null; } }; onUnmounted(() => { destroyChart(); }); return { chart, isLoading, error, createChart, destroyChart, }; } ``` ### Using the Composable ```vue ``` ## Responsive Design ### Mobile-Optimized Component ```vue ``` ## Theming Integration ### Vue 3 with CSS Variables ```vue ``` ## Integration with Vue Router ```javascript // router/index.js import { createRouter, createWebHistory } from "vue-router"; import ChartView from "@/views/ChartView.vue"; const routes = [ { path: "/chart/:symbol?", name: "Chart", component: ChartView, props: (route) => ({ symbol: route.params.symbol || "NASDAQ:AAPL", interval: route.query.interval || "1D", }), }, ]; export default createRouter({ history: createWebHistory(), routes, }); ``` ```vue ``` ## Related Documentation - **[CodePen Live Demo](https://codepen.io/Admin-GoCharting/pen/vue-demo)** - Try the Vue.js example - **[Quick Start Guide](../tutorials/quick-start.md)** - Get started with the SDK - **[React Integration](./react-integration.md)** - React component examples - **[API Reference](../api/chart.md)** - Complete API documentation - **[Configuration Guide](../api/configuration.md)** - All configuration options ## Troubleshooting ### Common Issues **Chart not rendering:** - Ensure the container ref is properly set - Check that the component is mounted before initializing - Verify the license key is valid **Reactivity not working:** - Use `ref()` for reactive values - Ensure watchers are properly set up - Check that props are being passed correctly **Memory leaks:** - Always destroy charts in `onUnmounted` - Remove event listeners properly - Clear any intervals or timeouts --- _Ready to use Vue.js with GoCharting SDK in production? Get your license key at [gocharting.com](https://gocharting.com)!_ } .status.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } @media (max-width: 768px) { .chart-controls { flex-direction: column; } .control-group { width: 100%; } } ``` ``` File: docs/guides/add-custom-button-to-top-toolbar.md ================================================================ # Add custom button to top toolbar Add your own controls to the chart top bar with `headerReady`, `createButton`, and `createDropdown`. ## Prerequisites - Chart created with `createChart` (or ``) - Top bar visible (not fully excluded via `exclude`) ## Wait for the header Always await `headerReady()` before creating buttons. It resolves when the top bar is mounted (or immediately if the top bar is hidden). ```javascript const chart = GoChartingSDK.createChart("#chart", { datafeed: myDatafeed, symbol: "NASDAQ:AAPL", interval: "1D", licenseKey: "YOUR_LICENSE_KEY", onReady: async () => { await chart.headerReady(); addToolbarControls(chart); }, }); ``` ## Create a button `createButton` returns a real `HTMLButtonElement`. Set label, tooltip, and click handler like any DOM button. ```javascript async function addToolbarControls(chart) { await chart.headerReady(); const btn = chart.createButton({ align: "left" }); btn.textContent = "Ping"; btn.title = "Custom action"; btn.addEventListener("click", () => { console.log("ping"); }); } ``` | Option | Type | Notes | | ------ | ---- | ----- | | `align` | `"left"` \| `"right"` | Placement in the top bar | | `text` | `string` | Optional initial label | | `title` | `string` | Tooltip | | `onClick` | `() => void` | Optional click handler | | `useCompactStyle` | `boolean` | Compact button chrome | ## Create a dropdown `createDropdown` returns a promise with `{ applyOptions, remove }`. ```javascript const dd = await chart.createDropdown({ title: "TF", tooltip: "Quick timeframe", align: "right", items: [ { title: "5m", onSelect: () => chart.setInterval("5m") }, { title: "1h", onSelect: () => chart.setInterval("1h") }, { title: "1D", onSelect: () => chart.setInterval("1D") }, ], }); // Later: dd.applyOptions({ title: "Interval" }); // dd.remove(); ``` ## Confirm before an action Pair a toolbar button with `showConfirmDialog` when the action is destructive or needs consent. ```javascript const clearBtn = chart.createButton({ align: "right", text: "Reset" }); clearBtn.addEventListener("click", async () => { const ok = await chart.showConfirmDialog({ title: "Reset chart?", body: "This clears drawings on the active chart.", }); if (!ok) return; // perform reset… }); ``` ## Remove a button ```javascript chart.removeButton(btn); ``` ## Full example ```javascript async function addToolbarControls(chart) { await chart.headerReady(); const alertBtn = chart.createButton({ align: "left", text: "Alert", title: "Create price alert", }); alertBtn.addEventListener("click", () => { console.log("open alert UI"); }); await chart.createDropdown({ title: "Theme", align: "right", items: [ { title: "Light", onSelect: () => chart.setTheme("light") }, { title: "Dark", onSelect: () => chart.setTheme("dark") }, ], }); } ``` ## Related - [Chart API](../api/chart.md) — `headerReady`, `createButton`, `createDropdown`, `removeButton` - [Configuration](./configuration.md) — `exclude` and top-bar options File: docs/guides/autofit.md ======================================= # Built-in AutoFit Guide The GoCharting SDK now includes **built-in AutoFit functionality** that makes chart sizing bulletproof and completely automatic. No more CSS headaches, no more sizing issues - just perfect charts that work everywhere. ## What is AutoFit? AutoFit is a comprehensive sizing solution that: - ** Automatically detects container dimensions** and fits charts perfectly - ** Responds to window resizing** in real-time using ResizeObserver - ** Handles all edge cases** like zero dimensions, missing containers, etc. - ** Injects CSS automatically** so you don't need external stylesheets - ** Works out of the box** with zero configuration required ## Key Benefits ### Before AutoFit (Old Way) ```javascript // Complex setup required import { ProfessionalChart } from "@gocharting/chart-sdk"; import "./chart-styles.css"; // External CSS needed // Manual container setup const container = document.getElementById("chart"); container.style.width = "100%"; container.style.height = "600px"; container.style.position = "relative"; // Manual resize handling window.addEventListener("resize", () => { chart.resize(); }); // Chart creation const chart = new ProfessionalChart({ container: container, width: container.offsetWidth, height: container.offsetHeight, // ... other options }); ``` ### After AutoFit (New Way) ```javascript // Simple, bulletproof setup import { ProfessionalChart, React, ReactDOM } from "@gocharting/chart-sdk"; // Just create and render - AutoFit handles everything! const chart = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "NASDAQ:AAPL", interval: "1D", // autosize: true (enabled by default) }); ReactDOM.render(chart, document.getElementById("chart-container")); ``` ## How It Works ### 1. **Automatic CSS Injection** AutoFit automatically injects optimized CSS styles: ```css .gocharting-autofit-container { width: 100%; height: 500px; position: relative; overflow: hidden; box-sizing: border-box; min-height: 300px; max-height: 100vh; min-width: 300px; /* ... more bulletproof styles */ } ``` ### 2. **Smart Dimension Detection** ```javascript // AutoFit automatically: const rect = container.getBoundingClientRect(); const width = rect.width || container.offsetWidth || 800; const height = rect.height || container.offsetHeight || 500; // Validates dimensions if (width < 300) width = 300; if (height < 300) height = 300; ``` ### 3. **ResizeObserver Integration** ```javascript // Real-time responsive behavior const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { const { width, height } = entry.contentRect; updateChartDimensions(width, height); } }); ``` ## Configuration Options ### Basic Usage (Recommended) ```javascript // AutoFit enabled by default const chart = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "NASDAQ:AAPL", interval: "1D", }); ``` ### Custom Configuration ```javascript const chart = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "NASDAQ:AAPL", interval: "1D", // AutoFit options autosize: true, // Enable/disable AutoFit (default: true) width: "100%", // Override width (AutoFit still applies) height: 600, // Override height (AutoFit still applies) className: "my-chart", // Additional CSS class // Container styling style: { border: "1px solid #ddd", borderRadius: "8px", }, }); ``` ### Disable AutoFit (Not Recommended) ```javascript const chart = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "NASDAQ:AAPL", interval: "1D", autosize: false, // Disable AutoFit width: 800, // Manual width height: 600, // Manual height }); ``` ## Best Practices ### **Do This** ```html
``` ```javascript // Let AutoFit do its magic const chart = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "NASDAQ:AAPL", interval: "1D", }); ReactDOM.render(chart, document.getElementById("chart-container")); ``` ### **Avoid This** ```html
``` ```javascript // Don't disable AutoFit unless absolutely necessary const chart = React.createElement(ProfessionalChart, { autosize: false, // Loses all AutoFit benefits width: 800, height: 600, }); ``` ## Advanced Usage ### Custom Container Styling ```css /* You can still add custom styles */ #my-chart-container { border: 2px solid #007bff; border-radius: 12px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); margin: 20px; } ``` ### Responsive Breakpoints ```css /* AutoFit works with media queries */ @media (max-width: 768px) { #chart-container { height: 400px; /* AutoFit will adapt */ } } @media (max-width: 480px) { #chart-container { height: 300px; /* AutoFit will adapt */ } } ``` ### Multiple Charts ```javascript // Each chart gets its own AutoFit instance const chart1 = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "AAPL", }); const chart2 = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "MSFT", }); ReactDOM.render(chart1, document.getElementById("chart1")); ReactDOM.render(chart2, document.getElementById("chart2")); ``` ## Troubleshooting ### Chart Not Sizing Correctly ```javascript // Check container exists const container = document.getElementById("chart-container"); if (!container) { console.error("Chart container not found!"); } // Check container has dimensions const rect = container.getBoundingClientRect(); console.log("Container dimensions:", rect.width, "x", rect.height); ``` ### AutoFit Not Working ```javascript // Verify AutoFit is enabled const chart = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "NASDAQ:AAPL", interval: "1D", autosize: true, // Explicitly enable }); ``` ### Performance Issues ```javascript // AutoFit is optimized, but you can monitor performance console.time("Chart Render"); ReactDOM.render(chart, container); console.timeEnd("Chart Render"); ``` ## Migration from Old Approach ### Before (Manual sizing) ```javascript import { ProfessionalChart } from "@gocharting/chart-sdk"; const myDatafeed = { async getBars(symbolInfo, resolution, periodParams) { // Implementation return { bars }; }, resolveSymbol(symbolName, onResolve, onError) { const parts = String(symbolName).split(":"); const exchange = parts[0] || "NASDAQ"; const segment = parts.length >= 3 ? parts[1] : "EQUITY"; const symbol = parts[parts.length - 1] || "AAPL"; const name = symbol; const validIntervals = ["1m", "5m", "15m", "1h", "1D"]; onResolve({ exchange, segment, symbol, name, asset_type: "EQUITY", source_id: symbol, tradeable: true, is_index: false, is_formula: false, delay_seconds: 0, data_status: "streaming", contract_size: 1, tick_size: 0.01, display_tick_size: 0.01, volume_size_increment: 1, max_tick_precision: 2, max_volume_precision: 0, quote_currency: "USD", supports: { footprint: false }, exchange_info: { name: exchange.toLowerCase(), code: exchange, country_cd: "US", zone: "America/New_York", hours: [ { open: false }, { open: true }, { open: true }, { open: true }, { open: true }, { open: true }, { open: false }, ], valid_intervals: validIntervals, }, ticker: symbol, full_name: `${exchange}:${segment}:${symbol}`, description: name, type: "stock", session: "0930-1600", timezone: "America/New_York", has_intraday: true, has_daily: true, supported_resolutions: validIntervals, }); }, }; const chart = new ProfessionalChart({ container: document.getElementById("chart"), datafeed: myDatafeed, width: "100%", height: 600, }); ``` ### After (Composition-based with AutoFit) ```javascript import { ProfessionalChart, React, ReactDOM } from "@gocharting/chart-sdk"; const myDatafeed = { async getBars(symbolInfo, resolution, periodParams) { // Same implementation }, }; const chart = React.createElement(ProfessionalChart, { datafeed: myDatafeed, symbol: "NASDAQ:AAPL", interval: "1D", // AutoFit handles sizing automatically! }); ReactDOM.render(chart, document.getElementById("chart-container")); ``` ## What's Next? AutoFit is just the beginning! Future enhancements include: - ** Theme-aware sizing** - Different dimensions for light/dark themes - ** Content-aware sizing** - Automatic sizing based on chart content - ** Advanced responsive modes** - Breakpoint-specific configurations - ** Performance optimizations** - Even faster resize handling --- **Ready to experience bulletproof chart sizing?** AutoFit is enabled by default in all new ProfessionalChart instances! File: docs/guides/codepen-integration.md =================================================== # CodePen Integration Guide This guide explains how to embed CodePen examples in your GoCharting SDK documentation. ## Quick Start ### Option 1: Direct iframe Embedding (Recommended) For documentation systems that support HTML iframes: ```html ``` ### Option 2: Fallback Links (GitHub/GitLab) For platforms that don't support iframes: ```markdown [![Advanced Trading Demo](https://img.shields.io/badge/CodePen-Advanced%20Demo-000000?style=for-the-badge&logo=codepen&logoColor=white)](https://codepen.io/Admin-GoCharting/full/xbwvBbe) [![Preview](https://codepen.io/Admin-GoCharting/pen/xbwvBbe/image/large.png)](https://codepen.io/Admin-GoCharting/full/xbwvBbe) ``` ## Using the CodePen Component ### JavaScript Component Include the CodePen component in your documentation build: ```javascript // Import the component const { GoChartingCodePens } = require('./docs/components/CodePenEmbed.js'); // Generate advanced demo embed const advancedEmbed = GoChartingCodePens.advanced.generateMarkdown(); // Generate basic demo embed const basicEmbed = GoChartingCodePens.basic.generateMarkdown(); ``` ### Custom Embed Creation ```javascript const { createCodePenEmbed } = require('./docs/components/CodePenEmbed.js'); const customEmbed = createCodePenEmbed('your-pen-id', { title: 'Your Custom Demo', height: 500, theme: 'dark', description: 'Description of your demo', features: [ ' Feature 1', ' Feature 2', ' Feature 3' ] }); // Generate the embed const embedHTML = customEmbed.generateComplete(); ``` ## CodePen URL Parameters Customize the embedded CodePen behavior: | Parameter | Options | Description | |-----------|---------|-------------| | `default-tab` | `result`, `html`, `css`, `js` | Which tab to show by default | | `theme-id` | `dark`, `light` | CodePen theme | | `editable` | `true`, `false` | Allow editing in embed | | `height` | Number (pixels) | Height of the embed | ### Example URLs ``` Basic embed: https://codepen.io/Admin-GoCharting/embed/GgpVepq?default-tab=result With custom theme: https://codepen.io/Admin-GoCharting/embed/GgpVepq?default-tab=result&theme-id=dark Read-only embed: https://codepen.io/Admin-GoCharting/embed/GgpVepq?default-tab=result&editable=false ``` ## Responsive Design ### CSS for Responsive Embeds ```css .codepen-embed { width: 100%; max-width: 100%; margin: 2rem 0; } .codepen-embed iframe { width: 100%; border: 1px solid #e1e5e9; border-radius: 8px; } @media (max-width: 768px) { .codepen-embed iframe { height: 400px; } } ``` ## Build System Integration ### For Static Site Generators #### Hugo ```html ``` Usage: ```markdown {{< codepen id="xbwvBbe" title="Advanced Trading Demo" height="600" >}} ``` #### Jekyll ```liquid ``` Usage: ```markdown {% include codepen.html id="xbwvBbe" title="Advanced Trading Demo" height="600" %} ``` ### For React/Next.js ```jsx // components/CodePenEmbed.jsx import React from 'react'; const CodePenEmbed = ({ id, title, height = 500, theme = 'dark', tab = 'result', user = 'Admin-GoCharting' }) => { const src = `https://codepen.io/${user}/embed/${id}?default-tab=${tab}&theme-id=${theme}`; return (