version 4.2.0

This commit is contained in:
Mark Silverwood 2024-07-26 13:17:04 +01:00
parent 2ce43a329e
commit 23c42ebedd
No known key found for this signature in database
32 changed files with 3528 additions and 7 deletions

View File

@ -4,21 +4,21 @@ module.exports = [
{
name: 'CJS',
path: 'dist/lightweight-charts.production.cjs',
limit: '49.28 KB',
limit: '49.29 KB',
},
{
name: 'ESM',
path: 'dist/lightweight-charts.production.mjs',
limit: '49.21 KB',
limit: '49.22 KB',
},
{
name: 'Standalone-ESM',
path: 'dist/lightweight-charts.standalone.production.mjs',
limit: '50.93 KB',
limit: '50.94 KB',
},
{
name: 'Standalone',
path: 'dist/lightweight-charts.standalone.production.js',
limit: '50.97 KB',
limit: '50.98 KB',
},
];

View File

@ -1,6 +1,6 @@
{
"private": true,
"version": "4.1.8",
"version": "4.2.0",
"name": "lightweight-charts",
"author": "TradingView, Inc.",
"license": "Apache-2.0",

View File

@ -14,6 +14,16 @@ sidebar_position: 8
<!-- markdownlint-disable no-emphasis-as-heading -->
<!-- ^ using emphasis as headings so we don't have duplicate headers -->
## 4.2.0
**Enhancements**
- Added new [`attributionLogo`](https://tradingview.github.io/lightweight-charts/docs/api/interfaces/LayoutOptions#attributionLogo) option to `LayoutOptions`. This feature displays the TradingView attribution logo on the main chart pane by default, helping users meet the library's licensing requirements for attribution.
- The TradingView attribution logo can be easily hidden by setting the `attributionLogo` option to `false` in the chart's `layout` option.
- Improved data validation for `OhlcData` and `SingleValueData`. Introduced `isFulfilledBarData` for `OhlcData` and `isFulfilledLineData` for `SingleValueData`, ensuring more accurate validation of data types. Contributed by [@mozeryansky](https://github.com/mozeryansky) (PR [#1579](https://github.com/tradingview/lightweight-charts/pull/1579), fixes [#1526](https://github.com/tradingview/lightweight-charts/issues/1526)).
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.7..v4.2.0).
## 4.1.7
**Enhancements**

View File

@ -20,11 +20,12 @@
"@types/react": "^17.0",
"cross-env": "~7.0.3",
"docusaurus-plugin-typedoc": "0.18.0",
"lightweight-charts": "~4.1.0",
"lightweight-charts": "~4.2.0",
"lightweight-charts-local": "file:..",
"lightweight-charts-3.8": "npm:lightweight-charts@~3.8.0",
"lightweight-charts-4.0": "npm:lightweight-charts@~4.0.1",
"lightweight-charts-4.1": "npm:lightweight-charts@~4.1.7",
"lightweight-charts-4.2": "npm:lightweight-charts@~4.2.0",
"prism-react-renderer": "~1.3.5",
"raw-loader": "~4.0.2",
"react": "^17.0",

View File

@ -4,12 +4,14 @@ import type { Version } from '../../../../versions';
export type LightweightChartsApi38 = typeof import('lightweight-charts-3.8');
export type LightweightChartsApi40 = typeof import('lightweight-charts-4.0');
export type LightweightChartsApi41 = typeof import('lightweight-charts-4.1');
export type LightweightChartsApi42 = typeof import('lightweight-charts-4.2');
export type LightweightChartsApiCurrent = typeof import('../../../../..');
export interface LightweightChartsApiTypeMap {
'3.8': LightweightChartsApi38;
'4.0': LightweightChartsApi40;
'4.1': LightweightChartsApi41;
'4.2': LightweightChartsApi42;
current: LightweightChartsApiCurrent;
}
@ -17,6 +19,7 @@ export interface LightweightChartsCreateChartTypeMap {
'3.8': LightweightChartsApi38['createChart'];
'4.0': LightweightChartsApi40['createChart'];
'4.1': LightweightChartsApi41['createChart'];
'4.2': LightweightChartsApi42['createChart'];
current: LightweightChartsApiCurrent['createChart'];
}
@ -25,7 +28,7 @@ export type LightweightChartsVersion = Version | 'current';
export interface LightweightChartsApiGetterResult<T extends keyof LightweightChartsApiTypeMap> {
module: LightweightChartsApiTypeMap[T];
createChart: LightweightChartsApiTypeMap[T]['createChart'];
createChartEx: T extends '4.1' | 'current' ? LightweightChartsApiTypeMap[T]['createChartEx'] : undefined;
createChartEx: T extends '4.2' | '4.1' | 'current' ? LightweightChartsApiTypeMap[T]['createChartEx'] : undefined;
}
export type LightweightChartsApiGetters = {
@ -81,6 +84,23 @@ export const importLightweightChartsVersion: LightweightChartsApiGetters = {
return { module, createChart, createChartEx: createChartEx as typeof module.createChartEx };
},
4.2: async (window: Window) => {
const module = await import('lightweight-charts-4.2');
const createChart: typeof module.createChart = (container: string | HTMLElement, options?: Parameters<typeof module.createChart>[1]) => {
const result = module.createChart(container, options);
addResizeHandler(window, container as HTMLElement, result.resize.bind(result));
return result;
};
const createChartEx = (container: string | HTMLElement, behaviour: Parameters<typeof module.createChartEx>[1], options?: Parameters<typeof module.createChartEx>[2]) => {
const result = module.createChartEx(container, behaviour, options);
addResizeHandler(window, container as HTMLElement, result.resize.bind(result));
return result;
};
return { module, createChart, createChartEx: createChartEx as typeof module.createChartEx };
},
current: async () => {
const module = await import('../../../../..');

View File

@ -0,0 +1,125 @@
---
id: android
description: You can use Lightweight Charts™ inside an Android application. To use Lightweight Charts™ in that context, you can use our Android wrapper, which will allow you to interact with Lightweight Charts™ library, which will be rendered in a web view.
keywords:
- charts
- android
- canvas
- charting library
- charting
- html5 charts
- financial charting library
sidebar_position: 7
---
# Android wrapper
:::note
You can find the source code of the Lightweight Charts™ Android wrapper in [this repository](https://github.com/tradingview/lightweight-charts-android).
:::
:::info
This wrapper is currently still using `v3.8.0`. This will be updated to `v4.0.0` in the near future.
:::
You can use Lightweight Charts™ inside an Android application. To use Lightweight Charts™ in that context, you can use our Android wrapper, which will allow you to interact with Lightweight Charts™ library, which will be rendered in a web view.
## Installation
:::info
Requires minSdkVersion 21, and installed WebView with support of ES6
:::
In `/build.gradle`
```groovy
allprojects {
repositories {
google()
mavenCentral()
}
}
```
In `/gradle_module/build.gradle`
```groovy
dependencies {
//...
implementation 'com.tradingview:lightweightcharts:3.8.0'
}
```
## Usage
Add view to the layout.
```xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tradingview.lightweightcharts.view.ChartsView
android:id="@+id/charts_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
```
Configure the chart layout.
```kotlin
charts_view.api.applyOptions {
layout = layoutOptions {
background = SolidColor(Color.LTGRAY)
textColor = Color.BLACK.toIntColor()
}
localization = localizationOptions {
locale = "ru-RU"
priceFormatter = PriceFormatter(template = "{price:#2:#3}$")
timeFormatter = TimeFormatter(
locale = "ru-RU",
dateTimeFormat = DateTimeFormat.DATE_TIME
)
}
}
```
Add any series to the chart and store a reference to it.
```kotlin
lateinit var histogramSeries: SeriesApi
charts_view.api.addHistogramSeries(
onSeriesCreated = { series ->
histogramSeries = series
}
)
```
Add data to the series.
```kotlin
val data = listOf(
HistogramData(Time.BusinessDay(2019, 6, 11), 40.01f),
HistogramData(Time.BusinessDay(2019, 6, 12), 52.38f),
HistogramData(Time.BusinessDay(2019, 6, 13), 36.30f),
HistogramData(Time.BusinessDay(2019, 6, 14), 34.48f),
WhitespaceData(Time.BusinessDay(2019, 6, 15)),
WhitespaceData(Time.BusinessDay(2019, 6, 16)),
HistogramData(Time.BusinessDay(2019, 6, 17), 41.50f),
HistogramData(Time.BusinessDay(2019, 6, 18), 34.82f)
)
histogramSeries.setData(data)
```
## How to run the provided example
The [GitHub repository](https://github.com/tradingview/lightweight-charts-android) for lightweight-charts-android contains an example of the library in action.
You can run the example (LighweightCharts.app) by cloning the repository and opening it in Android Studio. You will need to have [NodeJS/NPM](https://nodejs.org/) installed.

View File

@ -0,0 +1,196 @@
---
slug: /
id: intro
sidebar_position: 0
---
# Getting started
## Requirements
First of all, Lightweight Charts™ is _a client-side_ library.
This means that it does not and cannot work on the server-side (i.e. NodeJS), at least out of the box.
The code of `lightweight-charts` package targets the [_es2016_ language specification](https://262.ecma-international.org/7.0/).
Thus, all the browsers you will have to work with should support this language revision (see [this compatibility table](https://kangax.github.io/compat-table/es2016plus/)).
If you need to support the previous revisions, you could try to setup a transpilation of the package to the target you need to support in your build system (e.g. by using Babel).
If you'll have any issues with that, please raise an issue on github with the details and we'll investigate possible ways to solve it.
## Installation
The first thing you need to do to use `lightweight-charts` is to install it from [npm](https://www.npmjs.com/):
```console
npm install --save lightweight-charts
```
_Note that the package is shipped with TypeScript declarations, so you can easily use it within TypeScript code._
### Build variants
The library ships with the following build variants:
|Dependencies included|Mode|ES module|CommonJS ⚠️|IIFE (`window.LightweightCharts`)|
|-|-|-|-|-|
|No|PROD|`lightweight-charts.production.mjs`|`lightweight-charts.production.cjs`|N/A|
|No|DEV|`lightweight-charts.development.mjs`|`lightweight-charts.development.cjs`|N/A|
|Yes (standalone)|PROD|`lightweight-charts.standalone.production.mjs`|-|`lightweight-charts.standalone.production.js`|
|Yes (standalone)|DEV|`lightweight-charts.standalone.development.mjs`|-|`lightweight-charts.standalone.development.js`|
⚠️ **Deprecation note:** CommonJS support will be removed from the library at the start of 2024.
## License and attribution
:::tip
The Lightweight Charts™ license requires specifying TradingView as the product creator.
:::
You shall add the "attribution notice" from the [NOTICE](https://github.com/tradingview/lightweight-charts/blob/master/NOTICE) file and a link to <https://www.tradingview.com/> to the page of your website or mobile application that is available to your users.
As thanks for creating Lightweight Charts™, we'd be grateful if you add the attribution notice in a prominent place.
## Creating a chart
Once the library has been installed in your repo you're ready to create your first chart.
First of all, in a file where you would like to create a chart you need to import the library:
```js
import { createChart } from 'lightweight-charts';
```
[`createChart`](/api/index.md#createchart) is the entry-point for creating charts. You can use it to create as many charts as you need:
```js
import { createChart } from 'lightweight-charts';
// ...
// somewhere in your code
const firstChart = createChart(document.getElementById('firstContainer'));
const secondChart = createChart(document.getElementById('secondContainer'));
```
The result of this function is a [`IChartApi`](/api/interfaces/IChartApi.md) object, which you need to use to work with a chart instance.
## Creating a series
Once your chart is created it is ready to display data.
The basic primitive to display a data is [a series](/api/interfaces/ISeriesApi.md).
There are different types of series:
- Area
- Bar
- Baseline
- Candlestick
- Histogram
- Line
To create a series with desired type you need to use appropriate method from [`IChartApi`](/api/interfaces/IChartApi.md).
All of them have the same naming `add<type>Series`, where `<type>` is a type of a series you'd like to create:
```js
import { createChart } from 'lightweight-charts';
const chart = createChart(container);
const areaSeries = chart.addAreaSeries();
const barSeries = chart.addBarSeries();
const baselineSeries = chart.addBaselineSeries();
// ... and so on
```
Please look at [this page](/series-types.md) for more information about different series types.
Note that **a series cannot be transferred from one type to another one** since different series types have different data and options types.
## Setting and updating a data
Once your chart and series are created it's time to set data to the series.
Note that regardless of the series type, the API calls are the same (the type of the data might be different though).
### Setting the data to a series
To set the data (or to replace all data items) to a series you need to use [`ISeriesApi.setData`](/api/interfaces/ISeriesApi.md#setdata) method:
```js chart replaceThemeConstants
const chartOptions = { layout: { textColor: CHART_TEXT_COLOR, background: { type: 'solid', color: CHART_BACKGROUND_COLOR } } };
const chart = createChart(document.getElementById('container'), chartOptions);
const areaSeries = chart.addAreaSeries({
lineColor: LINE_LINE_COLOR, topColor: AREA_TOP_COLOR,
bottomColor: AREA_BOTTOM_COLOR,
});
areaSeries.setData([
{ time: '2018-12-22', value: 32.51 },
{ time: '2018-12-23', value: 31.11 },
{ time: '2018-12-24', value: 27.02 },
{ time: '2018-12-25', value: 27.32 },
{ time: '2018-12-26', value: 25.17 },
{ time: '2018-12-27', value: 28.89 },
{ time: '2018-12-28', value: 25.46 },
{ time: '2018-12-29', value: 23.92 },
{ time: '2018-12-30', value: 22.68 },
{ time: '2018-12-31', value: 22.67 },
]);
const candlestickSeries = chart.addCandlestickSeries({
upColor: BAR_UP_COLOR, downColor: BAR_DOWN_COLOR, borderVisible: false,
wickUpColor: BAR_UP_COLOR, wickDownColor: BAR_DOWN_COLOR,
});
candlestickSeries.setData([
{ time: '2018-12-22', open: 75.16, high: 82.84, low: 36.16, close: 45.72 },
{ time: '2018-12-23', open: 45.12, high: 53.90, low: 45.12, close: 48.09 },
{ time: '2018-12-24', open: 60.71, high: 60.71, low: 53.39, close: 59.29 },
{ time: '2018-12-25', open: 68.26, high: 68.26, low: 59.04, close: 60.50 },
{ time: '2018-12-26', open: 67.71, high: 105.85, low: 66.67, close: 91.04 },
{ time: '2018-12-27', open: 91.04, high: 121.40, low: 82.70, close: 111.40 },
{ time: '2018-12-28', open: 111.51, high: 142.83, low: 103.34, close: 131.25 },
{ time: '2018-12-29', open: 131.33, high: 151.17, low: 77.68, close: 96.43 },
{ time: '2018-12-30', open: 106.33, high: 110.20, low: 90.39, close: 98.10 },
{ time: '2018-12-31', open: 109.87, high: 114.69, low: 85.66, close: 111.26 },
]);
chart.timeScale().fitContent();
```
### Updating the data in a series
In a case when your data is updated (e.g. real-time updates) you might want to update the chart as well.
But using [`ISeriesApi.setData`](/api/interfaces/ISeriesApi.md#setdata) very often might affect the performance and we do not recommend to do this.
Also it replaces all series data with the new one, and probably this is not what you're looking for.
Thus, to update the data you can use a method [`ISeriesApi.update`](/api/interfaces/ISeriesApi.md#update).
It allows you to update the last data item or add a new one much faster without affecting the performance:
```js
import { createChart } from 'lightweight-charts';
const chart = createChart(container);
const areaSeries = chart.addAreaSeries();
areaSeries.setData([
// ... other data items
{ time: '2018-12-31', value: 22.67 },
]);
const candlestickSeries = chart.addCandlestickSeries();
candlestickSeries.setData([
// ... other data items
{ time: '2018-12-31', open: 109.87, high: 114.69, low: 85.66, close: 111.26 },
]);
// sometime later
// update the most recent bar
areaSeries.update({ time: '2018-12-31', value: 25 });
candlestickSeries.update({ time: '2018-12-31', open: 109.87, high: 114.69, low: 85.66, close: 112 });
// creating the new bar
areaSeries.update({ time: '2019-01-01', value: 20 });
candlestickSeries.update({ time: '2019-01-01', open: 112, high: 112, low: 100, close: 101 });
```

View File

@ -0,0 +1,106 @@
---
id: ios
description: You can use Lightweight Charts™ inside an iOS application. To use Lightweight Charts™ in that context, you can use our iOS wrapper, which will allow you to interact with Lightweight Charts™ library, which will be rendered in a web view.
keywords:
- charts
- iOS
- canvas
- charting library
- charting
- html5 charts
- financial charting library
sidebar_position: 6
---
# iOS wrapper
:::note
You can find the source code of the Lightweight Charts™ iOS wrapper in [this repository](https://github.com/tradingview/LightweightChartsIOS).
:::
:::info
This wrapper is currently still using `v3.8.0`. This will be updated to `v4.0.0` in the near future.
:::
You can use Lightweight Charts™ inside an iOS application. To use Lightweight Charts™ in that context, you can use our iOS wrapper, which will allow you to interact with Lightweight Charts™ library, which will be rendered in a web view.
## Installation
:::info
Requires iOS 10.0+
:::
### CocoaPods
[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate LightweightCharts into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
pod 'LightweightCharts', '~> 3.8.0'
```
### Swift Package Manager
The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler.
Once you have your Swift package set up, adding LightweightCharts as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
```swift
dependencies: [
.package(url: "https://github.com/tradingview/LightweightChartsIOS", .upToNextMajor(from: "4.0.0"))
]
```
## Usage
Once the library has been installed in your repo, you're ready to create your first chart.
First of all, in a file where you would like to create a chart, you need to import the library:
```swift
import LightweightCharts
```
Create instance of LightweightCharts, which is a subclass of UIView, and add it to your view.
```swift
var chart: LightweightCharts!
// ...
chart = LightweightCharts()
view.addSubview(chart)
// ... setup layout
```
Add any series to the chart and store a reference to it.
```swift
var series: BarSeries!
// ...
series = chart.addBarSeries(options: nil)
```
Add data to the series.
```swift
let data = [
BarData(time: .string("2018-10-19"), open: 180.34, high: 180.99, low: 178.57, close: 179.85),
BarData(time: .string("2018-10-22"), open: 180.82, high: 181.40, low: 177.56, close: 178.75),
BarData(time: .string("2018-10-23"), open: 175.77, high: 179.49, low: 175.44, close: 178.53),
BarData(time: .string("2018-10-24"), open: 178.58, high: 182.37, low: 176.31, close: 176.97),
BarData(time: .string("2018-10-25"), open: 177.52, high: 180.50, low: 176.83, close: 179.07)
]
// ...
series.setData(data: data)
```
## How to run the provided example
The [GitHub repository](https://github.com/tradingview/LightweightChartsIOS) for LightweightChartsIOS contains an example of the library in action. To run the example, start by cloning the repository, go to the _Example_ directory, and then run
```sh
pod install
```

View File

@ -0,0 +1,2 @@
label: "Migration guides"
position: 5

View File

@ -0,0 +1,158 @@
# From v2 to v3
Lightweight Charts™ library 3.0 announces the major improvements: supporting two price scales and improving the time scale API.
In order of keep the API clear and consistent, we decided to allow breaking change of the API.
In this document you can find the migration guide from the previous version to 3.0.
## Time Scale API
Previously, to handle changing visible time range you needed to use `subscribeVisibleTimeRangeChange` and `unsubscribeVisibleTimeRangeChange` to subscribe and unsubscribe from visible range events.
These methods were available in the chart object (e.g. you call it like `chart.subscribeVisibleTimeRangeChange(func)`).
In 3.0 in order to make API more consistent with the new API we decided to move these methods to [ITimeScaleApi](/api/interfaces/ITimeScaleApi.md)
(along with the new subscription methods [`ITimeScaleApi.subscribeVisibleLogicalRangeChange`](/api/interfaces/ITimeScaleApi.md#subscribevisiblelogicalrangechange) and [`ITimeScaleApi.unsubscribeVisibleLogicalRangeChange`](/api/interfaces/ITimeScaleApi.md#unsubscribevisiblelogicalrangechange)).
So, to migrate your code to 3.0 you just need to replace:
- `chart.subscribeVisibleTimeRangeChange` with `chart.timeScale().subscribeVisibleTimeRangeChange`
- `chart.unsubscribeVisibleTimeRangeChange` with `chart.timeScale().unsubscribeVisibleTimeRangeChange`
## Two price scales
We understand disadvantages of breaking changes in the API, so we have not removed support of the current API at all, but have deprecated it, so the most common cases will continue to work.
You can refer to the new API [here](../price-scale.md).
Following are migration rules.
### Default behavior
Default behavior is not changed. If you do not specify price scale options, the chart will have the right price scale visible and all the series will assign to it.
### Left price scale
If you need the price scale to be drawn on the left side, you should make the following changes.
instead of
```js
const chart = LightweightCharts.createChart(container, {
priceScale: {
position: 'left',
},
});
```
use
```js
const chart = LightweightCharts.createChart(container, {
rightPriceScale: {
visible: false,
},
leftPriceScale: {
visible: true,
},
});
```
then specify target price scale while creating a series:
```js
const histSeries = chart.addHistogramSeries({
priceScaleId: 'left',
});
```
New version fully supports this case via the old API, however this support will be removed in the future releases.
### No price scale
To create chart without any visible price scale, instead of
```js
const chart = LightweightCharts.createChart(container, {
priceScale: {
position: 'none',
},
});
```
use
```js
const chart = LightweightCharts.createChart(container, {
leftPriceScale: {
visible: false,
},
rightPriceScale: {
visible: false,
},
});
```
New version fully supports this case via the old API, however this support will be removed in the future releases.
### Creating overlay
To create an overlay series, instead of
```js
const histogramSeries = chart.addHistogramSeries({
overlay: true,
});
```
use
```js
const histogramSeries = chart.addHistogramSeries({
// or any other _the same_ id for all overlay series
priceScaleId: '',
});
```
New version fully supports this case via the old API, however this support will be removed in the future releases.
### Move price scale from right to left or vice versa
To do this, instead of
```js
const chart = LightweightCharts.createChart(container);
const mainSeries = chart.addLineSeries();
// ...
chart.applyOptions({
priceScale: {
position: 'left',
},
});
```
use
```js
const chart = LightweightCharts.createChart(container);
const mainSeries = chart.addLineSeries();
// ...
chart.applyOptions({
leftPriceScale: {
visible: true,
},
rightPriceScale: {
visible: false,
},
});
mainSeries.applyOptions({
priceScaleId: 'left',
});
```
New version does not support this case via the old API, so, if you use it, you should migrate your code in order of keeping it working.

View File

@ -0,0 +1,200 @@
# From v3 to v4
In this document you can find the migration guide from the previous version v3 to v4.
## Exported enum `LasPriceAnimationMode` has been removed
Please use [`LastPriceAnimationMode`](/api/enums/LastPriceAnimationMode.md) instead.
## `scaleMargins` option has been removed from series options
Previously, you could do something like the following:
```js
const series = chart.addLineSeries({
scaleMargins: { /* options here */},
});
```
And `scaleMargins` option was applied to series' price scale as `scaleMargins` option.
Since v4 this option won't be applied to the price scale and will be just ignored (if you're using TypeScript you will get a compilation error).
To fix this, you need to apply these options to series' price scale:
```js
const series = chart.addLineSeries();
series.priceScale().applyOptions({
scaleMargins: { /* options here */},
});
```
## `backgroundColor` from `layout` options has been removed
If you want to have solid background color you need to use [`background`](/api/interfaces/LayoutOptions.md#background) property instead, e.g. instead of:
```js
const chart = createChart({
layout: {
backgroundColor: 'red',
},
});
```
use
```js
const chart = createChart({
layout: {
background: {
type: ColorType.Solid,
color: 'red',
},
},
});
```
## `overlay` property of series options has been removed
Please follow [the guide for migrating from v2 to v3](./from-v2-to-v3.md#creating-overlay) where this option was deprecated.
## `priceScale` option has been removed
Please follow [the guide for migrating from v2 to v3](./from-v2-to-v3.md#two-price-scales).
## `priceScale()` method of chart API now requires to provide price scale id
Before v4 you could write the following code:
```js
const priceScale = chart.priceScale();
```
And in `priceScale` you had a right price scale if it is visible and a left price scale otherwise.
Since v4 you have to provide an ID of price scale explicitly, e.g. if you want to get a right price scale you need to provide `'right'`:
```js
const rightPriceScale = chart.priceScale('right');
const leftPriceScale = chart.priceScale('left');
```
## `drawTicks` from `leftPriceScale` and `rightPriceScale` options has been renamed to `ticksVisible`
Since v4 you have to use `ticksVisible` instead of `drawTicks`.
```js
const chart = createChart({
leftPriceScale: {
ticksVisible: false,
},
rightPriceScale: {
ticksVisible: false,
},
});
```
Also this option is off by default.
## The type of outbound time values has been changed
Affected API:
- [`IChartApi.subscribeClick`](/api/interfaces/IChartApi.md#subscribeclick) (via [`MouseEventParams.time`](/api/interfaces/MouseEventParams.md#time))
- [`IChartApi.subscribeCrosshairMove`](/api/interfaces/IChartApi.md#subscribecrosshairmove) (via [`MouseEventParams.time`](/api/interfaces/MouseEventParams.md#time))
- [`LocalizationOptions.timeFormatter`](/api/interfaces/LocalizationOptions.md#timeformatter) (via argument of [`TimeFormatterFn`](/api/index.md#timeformatterfn))
- [`TimeScaleOptions.tickMarkFormatter`](/api/interfaces/TimeScaleOptions.md#tickmarkformatter) (via argument of [`TickMarkFormatter`](/api/index.md#tickmarkformatter))
Previously the type of an inbound time (a values you provide to the library, e.g. in [`ISeriesApi.setData`](/api/interfaces/ISeriesApi.md#setdata)) was different from an outbound one (a values the library provides to your code, e.g. an argument of [`LocalizationOptions.timeFormatter`](/api/interfaces/LocalizationOptions.md#timeformatter)).
So the difference between types was that outbound time couldn't be a business day string.
Since v4 we improved our API in this matter and now the library will return exactly the same values back for all time-related properties.
Thus, if you provide a string to your series in [`ISeriesApi.setData`](/api/interfaces/ISeriesApi.md#setdata), you'll receive exactly the same value back:
```js
series.setData([
{ time: '2001-01-01', value: 1 },
]);
chart.applyOptions({
localization: {
timeFormatter: time => time, // will be '2001-01-01' for the bar above
},
timeScale: {
tickMarkFormatter: time => time, // will be '2001-01-01' for the bar above
},
});
chart.subscribeCrosshairMove(param => {
console.log(param.time); // will be '2001-01-01' if you hover the bar above
});
chart.subscribeClick(param => {
console.log(param.time); // will be '2001-01-01' if you click on the bar above
});
```
Handling this breaking change depends on your needs and your handlers, but generally speaking you need to convert provided time to a desired format manually if it is required.
For example, you could use provided helpers to check the type of a time:
```js
import {
createChart,
isUTCTimestamp,
isBusinessDay,
} from 'lightweight-charts';
const chart = createChart(document.body);
chart.subscribeClick(param => {
if (param.time === undefined) {
// the time is undefined, i.e. there is no any data point where a time could be received from
return;
}
if (isUTCTimestamp(param.time)) {
// param.time is UTCTimestamp
} else if (isBusinessDay(param.time)) {
// param.time is a BusinessDay object
} else {
// param.time is a business day string in ISO format, e.g. `'2010-01-01'`
}
});
```
## `seriesPrices` property from `MouseEventParams` has been removed
Affected API:
- [`IChartApi.subscribeClick`](/api/interfaces/IChartApi.md#subscribeclick)
- [`IChartApi.subscribeCrosshairMove`](/api/interfaces/IChartApi.md#subscribecrosshairmove)
The property `seriesPrices` of [`MouseEventParams`](/api/interfaces/MouseEventParams.md) has been removed.
Instead, you can use [`MouseEventParams.seriesData`](/api/interfaces/MouseEventParams.md#seriesdata) - it is pretty similar to the old `seriesPrices`, but it contains series' data items instead of just prices:
```js
lineSeries.setData([{ time: '2001-01-01', value: 1 }]);
barSeries.setData([{ time: '2001-01-01', open: 5, high: 10, low: 1, close: 7 }]);
chart.subscribeCrosshairMove(param => {
console.log(param.seriesData.get(lineSeries)); // { time: '2001-01-01', value: 1 } or undefined
console.log(param.seriesData.get(barSeries)); // { time: '2001-01-01', open: 5, high: 10, low: 1, close: 7 } or undefined
});
```
## `MouseEventParams` field `hoveredMarkerId` was renamed to `hoveredObjectId`
Since v4 you have to use `hoveredObjectId` instead of `hoveredMarkerId`.
```js
chart.subscribeCrosshairMove(param => {
console.log(param.hoveredObjectId);
});
chart.subscribeClick(param => {
console.log(param.hoveredObjectId);
});
```

View File

@ -0,0 +1,6 @@
module.exports = {
globals: {
document: false,
createChart: false,
},
};

View File

@ -0,0 +1,141 @@
---
sidebar_label: Canvas Rendering Target
sidebar_position: 3
---
# Canvas Rendering Target
The renderer functions used within the plugins (both Custom Series, and Drawing
Primitives) are provided with a `CanvasRenderingTarget2D` interface on which the
drawing logic (using the
[Browser's 2D Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D))
should be executed. `CanvasRenderingTarget2D` is provided by the
[Fancy Canvas](https://github.com/tradingview/fancy-canvas) library.
:::info
The typescript definitions can be viewed here:
- [fancy-canvas on npmjs.com](https://www.npmjs.com/package/fancy-canvas?activeTab=code)
and specifically the definition for `CanvasRenderingTarget2D` can be viewed
here:
- [canvas-rendering-target.d.ts](https://unpkg.com/fancy-canvas/canvas-rendering-target.d.ts)
:::
## Using `CanvasRenderingTarget2D`
`CanvasRenderingTarget2D` provides two rendering scope which you can use:
- `useMediaCoordinateSpace`
- `useBitmapCoordinateSpace`
## Difference between Bitmap and Media
Bitmap sizing represents the actual physical pixels on the device's screen,
while the media size represents the size of a pixel according to the operating
system (and browser) which is generally an integer representing the ratio of
actual physical pixels are used to render a media pixel. This integer ratio is
referred to as the device pixel ratio.
Using the bitmap sizing allows for more control over the drawn image to ensure
that the graphics are crisp and pixel perfect, however this generally means that
the code will contain a lot multiplication of coordinates by the pixel ratio. In
cases where you don't need to draw using the bitmap sizing then it is easier to
use media sizing as you don't need to worry about the devices pixel ratio.
### Bitmap Coordinate Space
`useBitmapCoordinateSpace` can be used to if you would like draw using the
actual devices pixels as the coordinate sizing. The provided scope (of type
`BitmapCoordinatesRenderingScope`) contains readonly values for the following:
- `context`
([CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)).
Context which can be used for rendering.
- `horizontalPixelRatio` (number)
- `verticalPixelRatio` (number)
- `bitmapSize` (Size). Height and width of the canvas in bitmap dimensions.
- `mediaSize` (Size). Height and width of the canvas in media dimensions.
#### Bitmap Coordinate Space Usage
```js title='javascript'
// target is an instance of CanvasRenderingTarget2D
target.useBitmapCoordinateSpace(scope => {
// scope is an instance of BitmapCoordinatesRenderingScope
// example of drawing a filled rectangle which fills the canvas
scope.context.beginPath();
scope.context.rect(0, 0, scope.bitmapSize.width, scope.bitmapSize.height);
scope.context.fillStyle = 'rgba(100, 200, 50, 0.5)';
scope.context.fill();
});
```
### Media Coordinate Space
`useMediaCoordinateSpace` can be used to if you would like draw using the media
dimensions as the coordinate sizing. The provided scope (of type
`MediaCoordinatesRenderingScope`) contains readonly values for the following:
- `context`
([CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)).
Context which can be used for rendering.
- `mediaSize` (Size). Height and width of the canvas in media dimensions.
#### Media Coordinate Space Usage
```js title='javascript'
// target is an instance of CanvasRenderingTarget2D
target.useMediaCoordinateSpace(scope => {
// scope is an instance of BitmapCoordinatesRenderingScope
// example of drawing a filled rectangle which fills the canvas
scope.context.beginPath();
scope.context.rect(0, 0, scope.mediaSize.width, scope.mediaSize.height);
scope.context.fillStyle = 'rgba(100, 200, 50, 0.5)';
scope.context.fill();
});
```
## General Tips
It is recommended that rendering functions should save and restore the canvas
context before and after all the rendering logic to ensure that the canvas state
is the same as when the renderer function was evoked. To handle the case
when an error in the code might prevent the restore function from being evoked,
you should use the try - finally code block to ensure that the context is
correctly restored in all cases.
**Note** that `useBitmapCoordinateSpace` and `useMediaCoordinateSpace` will automatically
save and restore the canvas context for the logic defined within them. This tip for your
additional rendering functions within the `use*CoordinateSpace`.
```js title='javascript'
function myRenderingFunction(scope) {
const ctx = scope.context;
// save the current state of the context to the stack
ctx.save();
try {
// example code
scope.context.beginPath();
scope.context.rect(0, 0, scope.mediaSize.width, scope.mediaSize.height);
scope.context.fillStyle = 'rgba(100, 200, 50, 0.5)';
scope.context.fill();
} finally {
// restore the saved context from the stack
ctx.restore();
}
}
target.useMediaCoordinateSpace(scope => {
myRenderingFunction(scope);
myOtherRenderingFunction(scope);
/* ... */
});
```

View File

@ -0,0 +1,129 @@
---
sidebar_label: Custom Series Types
sidebar_position: 2
---
# Custom Series Types
Custom series allow developers to create new types of series with their own data
structures, and rendering logic (implemented using
[CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
methods). These custom series extend the current capabilities of our built-in
series, providing a consistent API which mirrors the built-in chart types.
:::note
These series are expected to have a uniform width for each data point, which
ensures that the chart maintains a consistent look and feel across all series
types. The only restriction on the data structure is that it should extend the
[`CustomData`](../api/interfaces/CustomData.md) interface (have a valid time
property for each data point).
:::
## Defining a Custom Series
A custom series should implement the
[`ICustomSeriesPaneView`](../api/interfaces/ICustomSeriesPaneView.md) interface.
The interface defines the basic functionality and structure required for
creating a custom series view.
It includes the following methods and properties:
### Renderer
- ICustomSeriesPaneView property:
[`renderer`](../api/interfaces/ICustomSeriesPaneView.md#renderer)
This method should return a renderer which implements the
[`ICustomSeriesPaneRenderer`](../api/interfaces/ICustomSeriesPaneRenderer.md)
interface and is used to draw the series data on the main chart pane.
The [`draw`](../api/interfaces/ICustomSeriesPaneRenderer.md#draw) method of the
renderer is evoked whenever the chart needs to draw the series.
The [`PriceToCoordinateConverter`](../api/index.md#pricetocoordinateconverter)
provided as the 2nd argument to the draw method is a convenience function for
changing prices into vertical coordinate values. It is provided since the
series' original data will most likely be defined in price values, and the
renderer needs to draw with coordinates. The values returned by the converter
will be defined in mediaSize (unscaled by `devicePixelRatio`).
:::tip
`CanvasRenderingTarget2D` provided within the `draw` function is explained in
more detail on the [Canvas Rendering Target](./canvas-rendering-target) page.
:::
### Update
- ICustomSeriesPaneView property:
[`update`](../api/interfaces/ICustomSeriesPaneView.md#update)
This method will be called with the latest data for the renderer to use during
the next paint.
The update method is evoked with two parameters: `data` (discussed below), and
`seriesOptions`. seriesOptions is a reference to the currently applied options
for the series
The [`PaneRendererCustomData`](../api/interfaces/PaneRendererCustomData.md)
interface provides the data that can be used within the renderer for drawing the
series data. It includes the following properties:
- `bars`: List of all the series' items and their x coordinates. See
[`CustomBarItemData`](../api/interfaces/CustomBarItemData.md) for more details
- `barSpacing`: Spacing between consecutive bars.
- `visibleRange`: The current visible range of items on the chart.
### Price Value Builder
- ICustomSeriesPaneView property:
[`priceValueBuilder`](../api/interfaces/ICustomSeriesPaneView.md#priceValueBuilder)
A function for interpreting the custom series data and returning an array of
numbers representing the prices values for the item, specifically the equivalent
highest, lowest, and current price values for the data item.
These price values are used by the chart to determine the auto-scaling (to
ensure the items are in view) and the crosshair and price line positions. The
largest and smallest values in the array will be used to specify the visible
range of the painted item, and the last value will be used for the crosshair and
price line position.
### Whitespace
- ICustomSeriesPaneView property:
[`isWhitespace`](../api/interfaces/ICustomSeriesPaneView.md#iswhitespace)
A function used by the library to determine which data points provided by the
user should be considered Whitespace. The method should return `true` when the
data point is Whitespace. Data points which are whitespace data won't be provided to
the renderer, or the `priceValueBuilder`.
### Default Options
- ICustomSeriesPaneView property:
[`defaultOptions`](../api/interfaces/ICustomSeriesPaneView.md#defaultoptions)
The default options to be used for the series. The user can override these
values using the options argument in
[`addCustomSeries`](../api/interfaces/IChartApi.md#addcustomseries), or via the
[`applyOptions`](../api/interfaces/ISeriesApi.md#applyoptions) method on the
`ISeriesAPI`.
### Destroy
- ICustomSeriesPaneView property:
[`destroy`](../api/interfaces/ICustomSeriesPaneView.md#destroy)
This method will be evoked when the series has been removed from the chart. This
method should be used to clean up any objects, references, and other items that
could potentially cause memory leaks.
This method should contain all the necessary code to clean up the object before
it is removed from memory. This includes removing any event listeners or timers
that are attached to the object, removing any references to other objects, and
resetting any values or properties that were modified during the lifetime of the
object.

View File

@ -0,0 +1,263 @@
class PaneRenderer {
constructor(layer, showName, index, numBands) {
this._layer = layer;
this._showName = showName;
this._index = index;
this._selected = 'all';
this._numBands = numBands;
}
draw(target) {
if (this._layer.background) {
return;
}
if (this._selected !== 'all' && this._selected !== this._layer.id) {
return;
}
this._drawImpl(target);
}
drawBackground(target) {
if (!this._layer.background) {
return;
}
if (this._selected !== 'all' && this._selected !== this._layer.id) {
return;
}
this._drawImpl(target);
}
_drawingAngle(scope) {
const isPriceScale = scope.mediaSize.width < 100;
const isTimeScale = scope.mediaSize.height < 50;
if (isPriceScale) {
return 0;
}
if (isTimeScale) {
return Math.PI / 2;
}
return Math.PI / 3;
}
_drawImpl(target) {
target.useMediaCoordinateSpace(scope => {
const ctx = scope.context;
ctx.save();
if (this._selected === 'all') {
const isScale = scope.mediaSize.height < 50 || scope.mediaSize.width < 100;
const numBands = this._numBands + (isScale ? 2 : 0);
const angle = this._drawingAngle(scope);
const shift = Math.cos(angle) * scope.mediaSize.height;
const bandWidth = Math.round(
(scope.mediaSize.width - shift) / numBands
);
const offset = isScale ? 2 : 0;
const startX = (this._index + (isScale ? 1 : 0)) * bandWidth;
ctx.beginPath();
ctx.moveTo(startX, scope.mediaSize.height);
ctx.lineTo(startX + shift, offset);
ctx.lineTo(startX + shift + bandWidth, offset);
ctx.lineTo(startX + bandWidth, scope.mediaSize.height);
ctx.closePath();
ctx.fillStyle = this._layer.color;
ctx.fill();
if (this._showName) {
ctx.fillStyle = this._layer.textColor;
ctx.font = 'normal 16px sans-serif';
ctx.translate(startX, scope.mediaSize.height);
ctx.rotate(-1.06 * angle);
ctx.fillText(this._layer.name, 20, 20);
}
} else {
ctx.beginPath();
ctx.rect(0, 0, scope.mediaSize.width, scope.mediaSize.height);
ctx.fillStyle = this._layer.color;
ctx.fill();
}
ctx.restore();
});
}
update(name) {
this._selected = name;
}
}
class PaneView {
constructor(layer, showName, index, numBands) {
this._layer = layer;
this._renderer = new PaneRenderer(layer, showName, index, numBands);
}
zOrder() {
return this._layer.zOrder;
}
renderer() {
return this._renderer;
}
update(name) {
this._renderer.update(name);
}
}
class LayersPrimitive {
constructor() {
this.layers = {
bottom: {
name: 'bottom',
color: '#f72585',
textColor: '#ffffff',
zOrder: 'bottom',
background: false,
id: 'bottom',
},
normalBackground: {
name: 'normal (background)',
color: '#7209b7',
textColor: '#ffffff',
zOrder: 'normal',
background: true,
id: 'normalBackground',
},
normal: {
name: 'normal',
color: '#4361ee',
textColor: '#ffffff',
zOrder: 'normal',
background: false,
id: 'normal',
},
top: {
name: 'top',
color: '#4cc9f0',
textColor: '#000000',
zOrder: 'top',
background: false,
id: 'top',
},
};
const layerKeys = ['bottom', 'normalBackground', 'normal', 'top'];
const numBands = layerKeys.length;
this._paneViews = layerKeys.map(
(key, index) => new PaneView(this.layers[key], true, index, numBands)
);
this._pricePaneViews = layerKeys.map(
(key, index) => new PaneView(this.layers[key], false, index, numBands)
);
this._timePaneViews = layerKeys.map(
(key, index) => new PaneView(this.layers[key], false, index, numBands)
);
}
changeSelectedLayer(id) {
if (id !== 'all' && !Object.keys(this.layers).includes(id)) {
return;
}
this._paneViews.forEach(view => view.update(id));
this._pricePaneViews.forEach(view => view.update(id));
this._timePaneViews.forEach(view => view.update(id));
if (this._requestUpdate) {
this._requestUpdate();
}
}
attached({ requestUpdate }) {
this._requestUpdate = requestUpdate;
}
detached() {
this._requestUpdate = undefined;
}
updateAllViews() {}
paneViews() {
return this._paneViews;
}
timeAxisPaneViews() {
return this._timePaneViews;
}
priceAxisPaneViews() {
return this._pricePaneViews;
}
}
let randomFactor = 25 + Math.random() * 25;
const samplePoint = i =>
i *
(0.5 +
Math.sin(i / 10) * 0.2 +
Math.sin(i / 20) * 0.4 +
Math.sin(i / randomFactor) * 0.8 +
Math.sin(i / 500) * 0.5) +
200;
function generateLineData(numberOfPoints = 500) {
randomFactor = 25 + Math.random() * 25;
const res = [];
const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0, 0));
for (let i = 0; i < numberOfPoints; ++i) {
const time = date.getTime() / 1000;
const value = samplePoint(i);
res.push({
time,
value,
customValues: {
text: 'hello',
},
});
date.setUTCDate(date.getUTCDate() + 1);
}
return res;
}
const chartOptions = {
layout: {
textColor: CHART_TEXT_COLOR,
background: { type: 'solid', color: CHART_BACKGROUND_COLOR },
},
};
const chart = createChart(document.getElementById('container'), chartOptions);
const lineSeries = chart.addLineSeries({
color: CHART_TEXT_COLOR,
});
const data = generateLineData();
lineSeries.setData(data);
const layersPrimitive = new LayersPrimitive();
lineSeries.attachPrimitive(layersPrimitive);
function generateLayerOption(id, name, selected) {
const element = document.createElement('option');
element.value = id;
element.innerHTML = name;
element.selected = selected;
return element;
}
const chartContainer = document.querySelector('#container');
if (chartContainer) {
const layerSelect = document.createElement('select');
layerSelect.id = 'layer-select';
layerSelect.name = 'layer';
chartContainer.parentElement.appendChild(layerSelect);
layerSelect.style.position = 'absolute';
layerSelect.style.zIndex = 10;
layerSelect.style.left = '10px';
layerSelect.style.top = '10px';
}
const layerSelectDiv = document.querySelector('#layer-select');
// eslint-disable-next-line no-console
console.log(layerSelectDiv);
if (layerSelectDiv) {
layerSelectDiv.appendChild(generateLayerOption('all', 'All', true));
for (const layerInfo of Object.values(layersPrimitive.layers)) {
layerSelectDiv.appendChild(
generateLayerOption(layerInfo.id, layerInfo.name, false)
);
}
layerSelectDiv.addEventListener('change', () => {
layersPrimitive.changeSelectedLayer(layerSelectDiv.value);
});
}

View File

@ -0,0 +1,199 @@
/* eslint-disable max-classes-per-file */
class AxisView {
constructor(text, color, position) {
this._color = color;
this._text = text;
this._position = position;
}
coordinate() {
return this._position;
}
text() {
return this._text;
}
textColor() {
return '#FFFFFF';
}
backColor() {
return this._color;
}
}
class LegendPaneRenderer {
constructor(sections) {
this._sections = Object.values(sections);
}
draw(target) {
const count = this._sections.length;
const longestText = this._sections.reduce((longest, section) => {
if (section.name.length > longest.length) {
return section.name;
}
return longest;
}, '');
target.useMediaCoordinateSpace(scope => {
const ctx = scope.context;
const longestTextMeasurements = ctx.measureText(longestText);
ctx.beginPath();
ctx.roundRect(
20,
20,
longestTextMeasurements.width + 40,
(count + 0) * 20 + 10,
8
);
ctx.globalAlpha = 0.95;
ctx.fillStyle = '#FFFFFF';
ctx.fill();
ctx.globalAlpha = 1;
let currentY = 30;
this._sections.forEach(section => {
ctx.beginPath();
ctx.roundRect(30, currentY, 10, 10, 3);
ctx.fillStyle = section.color;
ctx.fill();
ctx.fillStyle = '#000000';
ctx.textBaseline = 'bottom';
ctx.fillText(section.name, 50, currentY + 10);
currentY += 20;
});
});
}
}
class LegendView {
constructor(sections) {
this._renderer = new LegendPaneRenderer(sections);
}
zOrder() {
return 'top';
}
renderer() {
return this._renderer;
}
}
class PaneRenderer {
constructor(color) {
this._color = color;
}
draw(target) {
target.useMediaCoordinateSpace(scope => {
const ctx = scope.context;
ctx.beginPath();
ctx.rect(0, 0, scope.mediaSize.width, scope.mediaSize.height);
ctx.globalAlpha = 0.3;
ctx.fillStyle = this._color;
ctx.fill();
ctx.globalAlpha = 0.6;
ctx.lineWidth = 8;
ctx.strokeStyle = this._color;
ctx.stroke();
ctx.globalAlpha = 1;
});
}
}
class PaneView {
constructor(color) {
this._renderer = new PaneRenderer(color);
}
zOrder() {
return 'bottom';
}
renderer() {
return this._renderer;
}
}
class SectionsPrimitive {
constructor() {
this.sections = {
pane: { color: '#4cc9f0', name: 'Chart Pane (paneViews)' },
price: { color: '#f72585', name: 'Price Pane (priceAxisPaneViews)' },
time: { color: '#4361ee', name: 'Time Pane (timeAxisPaneViews)' },
priceLabel: { color: '#f77f00', name: 'Price Label (priceAxisViews)' },
timeLabel: { color: '#40916c', name: 'Time Label (timeAxisViews)' },
};
this._paneViews = [
new PaneView(this.sections.pane.color),
new LegendView(this.sections),
];
this._pricePaneViews = [new PaneView(this.sections.price.color)];
this._timePaneViews = [new PaneView(this.sections.time.color)];
this._priceAxisViews = [
new AxisView('price label', this.sections.priceLabel.color, 80),
];
this._timeAxisViews = [
new AxisView('time label', this.sections.timeLabel.color, 200),
];
}
updateAllViews() {}
paneViews() {
return this._paneViews;
}
timeAxisPaneViews() {
return this._timePaneViews;
}
priceAxisPaneViews() {
return this._pricePaneViews;
}
timeAxisViews() {
return this._timeAxisViews;
}
priceAxisViews() {
return this._priceAxisViews;
}
}
let randomFactor = 25 + Math.random() * 25;
const samplePoint = i =>
i *
(0.5 +
Math.sin(i / 10) * 0.2 +
Math.sin(i / 20) * 0.4 +
Math.sin(i / randomFactor) * 0.8 +
Math.sin(i / 500) * 0.5) +
200;
function generateLineData(numberOfPoints = 500) {
randomFactor = 25 + Math.random() * 25;
const res = [];
const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0, 0));
for (let i = 0; i < numberOfPoints; ++i) {
const time = date.getTime() / 1000;
const value = samplePoint(i);
res.push({
time,
value,
customValues: {
text: 'hello',
},
});
date.setUTCDate(date.getUTCDate() + 1);
}
return res;
}
const chartOptions = {
layout: {
textColor: CHART_TEXT_COLOR,
background: { type: 'solid', color: CHART_BACKGROUND_COLOR },
},
};
const chart = createChart(document.getElementById('container'), chartOptions);
const lineSeries = chart.addLineSeries({
color: CHART_TEXT_COLOR,
});
const data = generateLineData();
lineSeries.setData(data);
lineSeries.attachPrimitive(new SectionsPrimitive());

View File

@ -0,0 +1,147 @@
---
sidebar_label: Introduction
sidebar_position: 0
---
# Plugins Introduction
The library provides a rich set of charting capabilities out of the box, but
developers can also extend its functionality by building custom plugins.
Plugins in Lightweight Charts™ come in two types:
[custom series](#custom-series) and [drawing primitives](#drawing-primitives).
Custom series allow developers to define new types of series, while drawing
primitives enable the creation of custom visualizations, drawing tools, and
chart annotations (and more) which can be attached to an existing series.
:::tip Picking between the Custom Series and Drawing Primitives
In the majority of cases you will most likely be better served by using a
[Drawing Primitive](#drawing-primitives) plugin unless you are specifically
looking to create a new type of series.
:::
With the flexibility provided by these plugins, developers can create highly
customizable charting applications for their users.
## Custom Series
Custom series allow developers to create new types of series with their own data
structures, and rendering logic (implemented using
[CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
methods). These custom series extend the current capabilities of our built-in
series, providing a consistent API which mirrors the built-in chart types. These
series are expected to have a uniform width for each data point, which ensures
that the chart maintains a consistent look and feel across all series types. The
only restriction on the data structure is that it should extend the
WhitespaceData interface (have a valid time property for each data point).
**You can find a more detailed guide to developing custom series in the
[Custom Series Types](./custom_series/) article.**
### Adding a custom series to a chart
A custom series can be added to a chart using the
[`addCustomSeries`](../api/interfaces/IChartApi.md#addcustomseries) method
which expects an instance of a class implementing the
[ICustomSeriesPaneView](../api/interfaces/ICustomSeriesPaneView.md) interface
as the first argument, and an optional set of options as the second argument.
The series can then be used just like any other series, for example you would
use `setData` method to provide data to the series.
```javascript title='javascript'
class MyCustomSeries {
/* Class implementing the ICustomSeriesPaneView interface */
}
// Create an instantiated custom series.
const customSeriesInstance = new MyCustomSeries();
const chart = createChart(document.getElementById('container'));
const myCustomSeries = chart.addCustomSeries(customSeriesInstance, {
// options for the MyCustomSeries
customOption: 10,
});
const data = [
{ time: 1642425322, value: 123, customValue: 456 },
/* ... more data */
];
myCustomSeries.setData(data);
```
## Drawing Primitives
Drawing primitives provide a more flexible approach to extending the charting
capabilities of Lightweight Charts™. They are attached to a specific series and
can draw anywhere on the chart, including the main chart pane, price scales, and
time scales.
Primitives can be used to create custom drawing tools or indicators, or to add
entirely new visualizations to the chart. Primitives can be drawn at different
levels in the visual stack, allowing for complex compositions of multiple
primitives.
**You can find a more detailed guide to developing series primitives in the
[Series Primitives](./series-primitives/) article.**
### Adding a primitive to an existing series
A custom series primitive can be added to an existing series using the
[`attachPrimitive()`](../api/interfaces/ISeriesApi.md#attachprimitive) method
which expects an instantiated object implementing the
[ISeriesPrimitive](../api/index.md#iseriesprimitive) interface as the first
argument.
```javascript title='javascript'
class MyCustomPrimitive {
/* Class implementing the ISeriesPrimitive interface */
}
// Create an instantiated series primitive.
const myCustomPrimitive = new MyCustomPrimitive();
const chart = createChart(document.getElementById('container'));
const lineSeries = chart.addLineSeries();
const data = [
{ time: 1642425322, value: 123 },
/* ... more data */
];
// Attach the primitive to the series
lineSeries.attachPrimitive(myCustomPrimitive);
```
### Adding a primitive to the chart instead of a series
It is required that a drawing primitive is attached to series on the chart. In some cases, it might not make sense to attach a primitive to a specific series on the chart, for example if you are dynamically adding and removing series but would like a specific primitive to remain on the chart always. If this is the case then it is recommended to create an empty series (of any type) and attach the primitive to that instead.
:::caution
This series wouldn't have data, and thus wouldn't have the concept of price values for the vertical positioning of items. In some cases, such as a watermark, this isn't an issue.
:::
**Example:**
```js title='javascript'
// ...
// Create an instantiated series primitive.
const myCustomPrimitive = new MyCustomPrimitive();
const chart = createChart(document.getElementById('container'));
// an empty series which won't ever have data
const chartSeries = chart.addLineSeries();
chartSeries.attachPrimitive(myCustomPrimitive);
```
## Examples
We have a few example plugins within the `plugin-examples` folder of the Lightweight Charts™ repo: [plugin-examples](https://github.com/tradingview/lightweight-charts/tree/master/plugin-examples).
You can view a demo site for these plugin examples here: [Plugin Examples Demos](https://tradingview.github.io/lightweight-charts/plugin-examples).

View File

@ -0,0 +1,2 @@
label: "Pixel Perfect Rendering"
position: 5

View File

@ -0,0 +1,101 @@
---
sidebar_position: 0
sidebar_label: Pixel Perfect Rendering
pagination_title: Pixel Perfect Rendering
title: Best Practices for Pixel Perfect Rendering in Canvas Drawings
description: Best Practices for Pixel Perfect Rendering in Canvas Drawings when creating plugins for the Lightweight Charts
keywords:
- plugins
- extensions
- rendering
- canvas
- bitmap
- media
- pixels
pagination_prev: null
---
To achieve crisp pixel perfect rendering for your plugins, it is recommended that the canvas drawings are created using bitmap coordinates. The difference between media and bitmap coordinate spaces is discussed on the [Canvas Rendering Target](../canvas-rendering-target.md) page. **Essentially, all drawing actions should use integer positions and dimensions when on the bitmap coordinate space.**
To ensure consistency between your plugins and the library's built-in logic for rendering points on the chart, use of the following calculation functions.
:::info
Variable names containing `media` refer to positions / dimensions specified using the media coordinate space (such as the x and y coordinates provided by the library to the renderers), and names containing `bitmap` refer to positions / dimensions on the bitmap coordinate space (actual device screen pixels).
:::
## Centered Shapes
If you need to draw a shape which is centred on a position (for example a price or x coordinate) and has a desired width then you could use the `positionsLine` function presented below. This can be used for drawing a horizontal line at a specific price, or a vertical line aligned with the centre of series point.
```typescript
interface BitmapPositionLength {
/** coordinate for use with a bitmap rendering scope */
position: number;
/** length for use with a bitmap rendering scope */
length: number;
}
function centreOffset(lineBitmapWidth: number): number {
return Math.floor(lineBitmapWidth * 0.5);
}
/**
* Calculates the bitmap position for an item with a desired length (height or width), and centred according to
* a position coordinate defined in media sizing.
* @param positionMedia - position coordinate for the bar (in media coordinates)
* @param pixelRatio - pixel ratio. Either horizontal for x positions, or vertical for y positions
* @param desiredWidthMedia - desired width (in media coordinates)
* @returns Position of the start point and length dimension.
*/
export function positionsLine(
positionMedia: number,
pixelRatio: number,
desiredWidthMedia: number = 1,
widthIsBitmap?: boolean
): BitmapPositionLength {
const scaledPosition = Math.round(pixelRatio * positionMedia);
const lineBitmapWidth = widthIsBitmap
? desiredWidthMedia
: Math.round(desiredWidthMedia * pixelRatio);
const offset = centreOffset(lineBitmapWidth);
const position = scaledPosition - offset;
return { position, length: lineBitmapWidth };
}
```
## Dual Point Shapes
If you need to draw a shape between two coordinates (for example, y coordinates for a high and low price) then you can use the `positionsBox` function as presented below.
```typescript
/**
* Determines the bitmap position and length for a dimension of a shape to be drawn.
* @param position1Media - media coordinate for the first point
* @param position2Media - media coordinate for the second point
* @param pixelRatio - pixel ratio for the corresponding axis (vertical or horizontal)
* @returns Position of the start point and length dimension.
*/
export function positionsBox(
position1Media: number,
position2Media: number,
pixelRatio: number
): BitmapPositionLength {
const scaledPosition1 = Math.round(pixelRatio * position1Media);
const scaledPosition2 = Math.round(pixelRatio * position2Media);
return {
position: Math.min(scaledPosition1, scaledPosition2),
length: Math.abs(scaledPosition2 - scaledPosition1) + 1,
};
}
```
## Default Widths
Please refer to the following pages for functions defining the default widths of shapes drawn by the library:
- [Crosshair and Grid Lines](./widths/crosshair.md)
- [Candlesticks](./widths/candlestick.md)
- [Columns (Histogram)](./widths/columns.md)
- [Full Bar Width](./widths/full-bar-width.md)

View File

@ -0,0 +1,2 @@
label: "Default Widths"
position: 0

View File

@ -0,0 +1,81 @@
---
sidebar_position: 0
sidebar_label: Candlesticks
pagination_title: Candlestick Widths
title: Candlestick Width Calculations
description: Describes the calculation for candlestick body widths
keywords:
- plugins
- extensions
- rendering
- canvas
- bitmap
- media
- pixels
- candlestick
- width
---
:::tip
It is recommend that you first read the [Pixel Perfect Rendering](../index.md) page.
:::
The following functions can be used to get the calculated width that the library would use for a candlestick at a specific bar spacing and device pixel ratio.
Below a bar spacing of 4, the library will attempt to use as large a width as possible without the possibility of overlapping, whilst above 4 then the width will start to trend towards an 80% width of the available space.
:::warning
It is expected that candles can overlap slightly at smaller bar spacings (more pronounced on lower resolution devices). This produces a more readable chart. If you need to ensure that bars can never overlap then rather use the widths for [Columns](./columns.md) or the [full bar width](./full-bar-width.md) calculation.
:::
```typescript
function optimalCandlestickWidth(
barSpacing: number,
pixelRatio: number
): number {
const barSpacingSpecialCaseFrom = 2.5;
const barSpacingSpecialCaseTo = 4;
const barSpacingSpecialCaseCoeff = 3;
if (barSpacing >= barSpacingSpecialCaseFrom && barSpacing <= barSpacingSpecialCaseTo) {
return Math.floor(barSpacingSpecialCaseCoeff * pixelRatio);
}
// coeff should be 1 on small barspacing and go to 0.8 while bar spacing grows
const barSpacingReducingCoeff = 0.2;
const coeff =
1 -
(barSpacingReducingCoeff *
Math.atan(
Math.max(barSpacingSpecialCaseTo, barSpacing) - barSpacingSpecialCaseTo
)) /
(Math.PI * 0.5);
const res = Math.floor(barSpacing * coeff * pixelRatio);
const scaledBarSpacing = Math.floor(barSpacing * pixelRatio);
const optimal = Math.min(res, scaledBarSpacing);
return Math.max(Math.floor(pixelRatio), optimal);
}
/**
* Calculates the candlestick width that the library would use for the current
* bar spacing.
* @param barSpacing bar spacing in media coordinates
* @param horizontalPixelRatio - horizontal pixel ratio
* @returns The width (in bitmap coordinates) that the chart would use to draw a candle body
*/
export function candlestickWidth(
barSpacing: number,
horizontalPixelRatio: number
): number {
let width = optimalCandlestickWidth(barSpacing, horizontalPixelRatio);
if (width >= 2) {
const wickWidth = Math.floor(horizontalPixelRatio);
if (wickWidth % 2 !== width % 2) {
width--;
}
}
return width;
}
```

View File

@ -0,0 +1,269 @@
---
sidebar_position: 0
sidebar_label: Columns
pagination_title: Histogram Column Widths
title: Histogram Column Width Calculations
description: Describes the calculation for histogram column widths
keywords:
- plugins
- extensions
- rendering
- canvas
- bitmap
- media
- pixels
- histogram
- column
- width
---
:::tip
It is recommend that you first read the [Pixel Perfect Rendering](../index.md) page.
:::
The following functions can be used to get the calculated width that the library would use for a histogram column at a specific bar spacing and device pixel ratio.
You can use the `calculateColumnPositionsInPlace` function instead of the `calculateColumnPositions` function to perform the calculation on an existing array of items without needing to create additional arrays (which is more efficient). It is recommended that you memoize the majority of the calculations below to improve the rendering performance.
```typescript
const alignToMinimalWidthLimit = 4;
const showSpacingMinimalBarWidth = 1;
/**
* Spacing gap between columns.
* @param barSpacingMedia - spacing between bars (media coordinate)
* @param horizontalPixelRatio - horizontal pixel ratio
* @returns Spacing gap between columns (in Bitmap coordinates)
*/
function columnSpacing(barSpacingMedia: number, horizontalPixelRatio: number) {
return Math.ceil(barSpacingMedia * horizontalPixelRatio) <=
showSpacingMinimalBarWidth
? 0
: Math.max(1, Math.floor(horizontalPixelRatio));
}
/**
* Desired width for columns. This may not be the final width because
* it may be adjusted later to ensure all columns on screen have a
* consistent width and gap.
* @param barSpacingMedia - spacing between bars (media coordinate)
* @param horizontalPixelRatio - horizontal pixel ratio
* @param spacing - Spacing gap between columns (in Bitmap coordinates). (optional, provide if you have already calculated it)
* @returns Desired width for column bars (in Bitmap coordinates)
*/
function desiredColumnWidth(
barSpacingMedia: number,
horizontalPixelRatio: number,
spacing?: number
) {
return (
Math.round(barSpacingMedia * horizontalPixelRatio) -
(spacing ?? columnSpacing(barSpacingMedia, horizontalPixelRatio))
);
}
interface ColumnCommon {
/** Spacing gap between columns */
spacing: number;
/** Shift columns left by one pixel */
shiftLeft: boolean;
/** Half width of a column */
columnHalfWidthBitmap: number;
/** horizontal pixel ratio */
horizontalPixelRatio: number;
}
/**
* Calculated values which are common to all the columns on the screen, and
* are required to calculate the individual positions.
* @param barSpacingMedia - spacing between bars (media coordinate)
* @param horizontalPixelRatio - horizontal pixel ratio
* @returns calculated values for subsequent column calculations
*/
function columnCommon(
barSpacingMedia: number,
horizontalPixelRatio: number
): ColumnCommon {
const spacing = columnSpacing(barSpacingMedia, horizontalPixelRatio);
const columnWidthBitmap = desiredColumnWidth(
barSpacingMedia,
horizontalPixelRatio,
spacing
);
const shiftLeft = columnWidthBitmap % 2 === 0;
const columnHalfWidthBitmap = (columnWidthBitmap - (shiftLeft ? 0 : 1)) / 2;
return {
spacing,
shiftLeft,
columnHalfWidthBitmap,
horizontalPixelRatio,
};
}
interface ColumnPosition {
left: number;
right: number;
shiftLeft: boolean;
}
/**
* Calculate the position for a column. These values can be later adjusted
* by a second pass which corrects widths, and shifts columns.
* @param xMedia - column x position (center) in media coordinates
* @param columnData - precalculated common values (returned by `columnCommon`)
* @param previousPosition - result from this function for the previous bar.
* @returns initial column position
*/
function calculateColumnPosition(
xMedia: number,
columnData: ColumnCommon,
previousPosition: ColumnPosition | undefined
): ColumnPosition {
const xBitmapUnRounded = xMedia * columnData.horizontalPixelRatio;
const xBitmap = Math.round(xBitmapUnRounded);
const xPositions: ColumnPosition = {
left: xBitmap - columnData.columnHalfWidthBitmap,
right:
xBitmap +
columnData.columnHalfWidthBitmap -
(columnData.shiftLeft ? 1 : 0),
shiftLeft: xBitmap > xBitmapUnRounded,
};
const expectedAlignmentShift = columnData.spacing + 1;
if (previousPosition) {
if (xPositions.left - previousPosition.right !== expectedAlignmentShift) {
// need to adjust alignment
if (previousPosition.shiftLeft) {
previousPosition.right = xPositions.left - expectedAlignmentShift;
} else {
xPositions.left = previousPosition.right + expectedAlignmentShift;
}
}
}
return xPositions;
}
function fixPositionsAndReturnSmallestWidth(
positions: ColumnPosition[],
initialMinWidth: number
): number {
return positions.reduce((smallest: number, position: ColumnPosition) => {
if (position.right < position.left) {
position.right = position.left;
}
const width = position.right - position.left + 1;
return Math.min(smallest, width);
}, initialMinWidth);
}
function fixAlignmentForNarrowColumns(
positions: ColumnPosition[],
minColumnWidth: number
) {
return positions.map((position: ColumnPosition) => {
const width = position.right - position.left + 1;
if (width <= minColumnWidth) return position;
if (position.shiftLeft) {
position.right -= 1;
} else {
position.left += 1;
}
return position;
});
}
/**
* Calculates the column positions and widths for the x positions.
* This function creates a new array. You may get faster performance using the
* `calculateColumnPositionsInPlace` function instead
* @param xMediaPositions - x positions for the bars in media coordinates
* @param barSpacingMedia - spacing between bars in media coordinates
* @param horizontalPixelRatio - horizontal pixel ratio
* @returns Positions for the columns
*/
export function calculateColumnPositions(
xMediaPositions: number[],
barSpacingMedia: number,
horizontalPixelRatio: number
): ColumnPosition[] {
const common = columnCommon(barSpacingMedia, horizontalPixelRatio);
const positions = new Array<ColumnPosition>(xMediaPositions.length);
let previous: ColumnPosition | undefined = undefined;
for (let i = 0; i < xMediaPositions.length; i++) {
positions[i] = calculateColumnPosition(
xMediaPositions[i],
common,
previous
);
previous = positions[i];
}
const initialMinWidth = Math.ceil(barSpacingMedia * horizontalPixelRatio);
const minColumnWidth = fixPositionsAndReturnSmallestWidth(
positions,
initialMinWidth
);
if (common.spacing > 0 && minColumnWidth < alignToMinimalWidthLimit) {
return fixAlignmentForNarrowColumns(positions, minColumnWidth);
}
return positions;
}
export interface ColumnPositionItem {
x: number;
column?: ColumnPosition;
}
/**
* Calculates the column positions and widths for bars using the existing
* array of items.
* @param items - bar items which include an `x` property, and will be mutated to contain a column property
* @param barSpacingMedia - bar spacing in media coordinates
* @param horizontalPixelRatio - horizontal pixel ratio
* @param startIndex - start index for visible bars within the items array
* @param endIndex - end index for visible bars within the items array
*/
export function calculateColumnPositionsInPlace(
items: ColumnPositionItem[],
barSpacingMedia: number,
horizontalPixelRatio: number,
startIndex: number,
endIndex: number
): void {
const common = columnCommon(barSpacingMedia, horizontalPixelRatio);
let previous: ColumnPosition | undefined = undefined;
for (let i = startIndex; i < Math.min(endIndex, items.length); i++) {
items[i].column = calculateColumnPosition(items[i].x, common, previous);
previous = items[i].column;
}
const minColumnWidth = (items as ColumnPositionItem[]).reduce(
(smallest: number, item: ColumnPositionItem, index: number) => {
if (!item.column || index < startIndex || index > endIndex)
return smallest;
if (item.column.right < item.column.left) {
item.column.right = item.column.left;
}
const width = item.column.right - item.column.left + 1;
return Math.min(smallest, width);
},
Math.ceil(barSpacingMedia * horizontalPixelRatio)
);
if (common.spacing > 0 && minColumnWidth < alignToMinimalWidthLimit) {
(items as ColumnPositionItem[]).forEach(
(item: ColumnPositionItem, index: number) => {
if (!item.column || index < startIndex || index > endIndex) return;
const width = item.column.right - item.column.left + 1;
if (width <= minColumnWidth) return item;
if (item.column.shiftLeft) {
item.column.right -= 1;
} else {
item.column.left += 1;
}
return item.column;
}
);
}
}
```

View File

@ -0,0 +1,54 @@
---
sidebar_position: 0
sidebar_label: Crosshair
pagination_title: Crosshair Widths
title: Crosshair and Grid Line Width Calculations
description: Describes the calculation for the crosshair line and grid line widths
keywords:
- plugins
- extensions
- rendering
- canvas
- bitmap
- media
- pixels
- crosshair
- grid
- line
- width
---
:::tip
It is recommend that you first read the [Pixel Perfect Rendering](../index.md) page.
:::
The following functions can be used to get the calculated width that the library would use for a crosshair or grid line at a specific device pixel ratio.
```typescript
/**
* Default grid / crosshair line width in Bitmap sizing
* @param horizontalPixelRatio - horizontal pixel ratio
* @returns default grid / crosshair line width in Bitmap sizing
*/
export function gridAndCrosshairBitmapWidth(
horizontalPixelRatio: number
): number {
return Math.max(1, Math.floor(horizontalPixelRatio));
}
/**
* Default grid / crosshair line width in Media sizing
* @param horizontalPixelRatio - horizontal pixel ratio
* @returns default grid / crosshair line width in Media sizing
*/
export function gridAndCrosshairMediaWidth(
horizontalPixelRatio: number
): number {
return (
gridAndCrosshairBitmapWidth(horizontalPixelRatio) / horizontalPixelRatio
);
}
```

View File

@ -0,0 +1,63 @@
---
sidebar_position: 0
sidebar_label: Full Bar Width
pagination_title: Full Bar Width
title: Full Bar Width Calculations
description: Describes the calculation for full bar widths
keywords:
- plugins
- extensions
- rendering
- canvas
- bitmap
- media
- pixels
- histogram
- column
- width
---
:::tip
It is recommend that you first read the [Pixel Perfect Rendering](../index.md) page.
:::
The following functions can be used to get the calculated width that the library would use for the full width of a bar (data point) at a specific bar spacing and device pixel ratio. This can be used when you would like to use the full width available for each data point on the x axis, and don't want any gaps to be visible.
```typescript
interface BitmapPositionLength {
/** coordinate for use with a bitmap rendering scope */
position: number;
/** length for use with a bitmap rendering scope */
length: number;
}
/**
* Calculates the position and width which will completely full the space for the bar.
* Useful if you want to draw something that will not have any gaps between surrounding bars.
* @param xMedia - x coordinate of the bar defined in media sizing
* @param halfBarSpacingMedia - half the width of the current barSpacing (un-rounded)
* @param horizontalPixelRatio - horizontal pixel ratio
* @returns position and width which will completely full the space for the bar
*/
export function fullBarWidth(
xMedia: number,
halfBarSpacingMedia: number,
horizontalPixelRatio: number
): BitmapPositionLength {
const fullWidthLeftMedia = xMedia - halfBarSpacingMedia;
const fullWidthRightMedia = xMedia + halfBarSpacingMedia;
const fullWidthLeftBitmap = Math.round(
fullWidthLeftMedia * horizontalPixelRatio
);
const fullWidthRightBitmap = Math.round(
fullWidthRightMedia * horizontalPixelRatio
);
const fullWidthBitmap = fullWidthRightBitmap - fullWidthLeftBitmap;
return {
position: fullWidthLeftBitmap,
length: fullWidthBitmap,
};
}
```

View File

@ -0,0 +1,169 @@
---
sidebar_label: Series Primitives
sidebar_position: 1
---
# Series Primitives
Primitives are extensions to the series which can define views and renderers to
draw on the chart using
[CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D).
Primitives are defined by implementing the
[`ISeriesPrimitive`](../api/index.md#iseriesprimitive) interface. The
interface defines the basic functionality and structure required for creating
custom primitives.
## Views
The primary purpose of a series primitive is to provide one, or more, views to
the library which contain the state and logic required to draw on the chart
panes.
There are two types of views which are supported within `ISeriesPrimitive` which
are:
- [`ISeriesPrimitivePaneView`](../api/interfaces/ISeriesPrimitivePaneView.md)
- [`ISeriesPrimitiveAxisView`](../api/interfaces/ISeriesPrimitiveAxisView.md)
The library will evoke the following getter functions (if defined) to get
references to the primitive's defined views for the corresponding section of the
chart:
- [`paneViews`](../api/interfaces/ISeriesPrimitiveBase.md#paneviews)
- [`priceAxisPaneViews`](../api/interfaces/ISeriesPrimitiveBase.md#priceaxispaneviews)
- [`timeAxisPaneViews`](../api/interfaces/ISeriesPrimitiveBase.md#timeaxispaneviews)
- [`priceAxisViews`](../api/interfaces/ISeriesPrimitiveBase.md#priceaxisviews)
- [`timeAxisViews`](../api/interfaces/ISeriesPrimitiveBase.md#timeaxisviews)
The first three views allow drawing on the corresponding panes (main chart pane,
price scale pane, and horizontal time scale pane) using the
[CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
and should implement the `ISeriesPrimitivePaneView` interface.
The views returned by the `priceAxisViews` and `timeAxisViews` getter methods
should implement the `ISeriesPrimitiveAxisView` interface and are used to define
labels to be drawn on the corresponding scales.
Below is a visual example showing the various sections of the chart where a
Primitive can draw.
import CodeBlock from '@theme/CodeBlock';
import sectionsExplainerCode from '!!raw-loader!./explainer-sections-demo.js';
<CodeBlock replaceThemeConstants chart className="language-js" chartOnly>
{sectionsExplainerCode}
</CodeBlock>
### ISeriesPrimitivePaneView
The [`ISeriesPrimitivePaneView`](../api/interfaces/ISeriesPrimitivePaneView.md)
interface can be used to define a view which provides a renderer (implementing
the
[`ISeriesPrimitivePaneRenderer`](../api/interfaces/ISeriesPrimitivePaneRenderer.md)
interface) for drawing on the corresponding area of the chart using the
[CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
API. The view can define a
[`zOrder`](../api/interfaces/ISeriesPrimitivePaneView.md#zorder) to control where
in the visual stack the drawing will occur (See
[`SeriesPrimitivePaneViewZOrder`](../api/index.md#seriesprimitivepaneviewzorder)
for more information).
Renderers should provide a
[`draw`](../api/interfaces/ISeriesPrimitivePaneRenderer.md#draw) method which will
be given a `CanvasRenderingTarget2D` target on which it can draw. Additionally,
a renderer can optionally provide a
[`drawBackground`](../api/interfaces/ISeriesPrimitivePaneRenderer.md#drawbackground)
method for drawing beneath other elements on the same zOrder.
:::tip
`CanvasRenderingTarget2D` is explained in more detail on the [Canvas Rendering Target](./canvas-rendering-target) page.
:::
#### Interactive Demo of zOrder layers
Below is an interactive demo chart illustrating where each zOrder is drawn
relative to the existing chart elements such as the grid, series, and crosshair.
import layersExplainerCode from '!!raw-loader!./explainer-layers-demo.js';
<CodeBlock replaceThemeConstants chart className="language-js" chartOnly>
{layersExplainerCode}
</CodeBlock>
### ISeriesPrimitiveAxisView
The [`ISeriesPrimitiveAxisView`](../api/interfaces/ISeriesPrimitiveAxisView.md)
interface can be used to define a label on the price or time axis.
This interface provides several methods to define the appearance and position of
the label, such as the
[`coordinate`](../api/interfaces/ISeriesPrimitiveAxisView.md#coordinate) method,
which should return the desired coordinate for the label on the axis. It also
defines optional methods to set the fixed coordinate, text, text color,
background color, and visibility of the label.
Please see the
[`ISeriesPrimitiveAxisView`](../api/interfaces/ISeriesPrimitiveAxisView.md)
interface for more details.
## Lifecycle Methods
Your primitive can use the
[`attached`](../api/interfaces/ISeriesPrimitiveBase.md#attached) and
[`detached`](../api/interfaces/ISeriesPrimitiveBase.md#detached) lifecycle methods to
manage the lifecycle of the primitive, such as creating or removing external
objects and event handlers.
### attached
This method is called when the primitive is attached to a chart. The attached
method is evoked with a
[single argument](../api/interfaces/SeriesAttachedParameter.md) containing
properties for the chart, series, and a callback to request an update. The
`chart` and `series` properties are references to the chart API and the series
API instances for convenience purposes so that they don't need to be manually
provided within the primitive's constructor (if needed by the primitive).
The `requestUpdate` callback allows the primitive to notify the chart that it
should be updated and redrawn.
### detached
This method is called when the primitive is detached from a chart. This can be
used to remove any external objects or event handlers that were created during
the attached lifecycle method.
## Updating Views
Your primitive should update the views in the
[`updateAllViews()`](../api/interfaces/ISeriesPrimitiveBase.md#updateallviews) method
such that when the renderers are evoked, they can draw with the latest
information. The library invokes this method when it wants to update and redraw
the chart. If you would like to notify the library that it should trigger an
update then you can use the `requestUpdate` callback provided by the attached
lifecycle method.
## Extending the Autoscale Info
The [`autoscaleInfo()`](../api/interfaces/ISeriesPrimitiveBase.md#autoscaleinfo)
method can be provided to extend the base autoScale information of the series.
This can be used to ensure that the chart is automatically scaled correctly to
include all the graphics drawn by the primitive.
Whenever the chart needs to calculate the vertical visible range of the series
within the current time range then it will evoke this method. This method can be
omitted and the library will use the normal autoscale information for the
series. If the method is implemented then the returned values will be merged
with the base autoscale information to define the vertical visible range.
:::warning
Please note that this method will be evoked very often during
scrolling and zooming of the chart, thus it is recommended that this method is
either simple to execute, or makes use of optimisations such as caching to
ensure that the chart remains responsive.
:::

View File

@ -0,0 +1,40 @@
---
sidebar_position: 2
---
# Price scale
Price Scale (or price axis) is a vertical scale that mostly maps prices to coordinates and vice versa.
The rules of converting depend on a price scale mode, a height of the chart and visible part of the data.
![Price scales](/img/price-scales.png "Price scales")
By default, chart has 2 predefined price scales: `left` and `right`, and an unlimited number of overlay scales.
Only `left` and `right` price scales could be displayed on the chart, all overlay scales are hidden.
If you want to change `left` price scale, you need to use [`leftPriceScale`](/api/interfaces/ChartOptionsBase.md#leftpricescale) option, to change `right` price scale use [`rightPriceScale`](/api/interfaces/ChartOptionsBase.md#rightpricescale), to change default options for an overlay price scale use [`overlayPriceScales`](/api/interfaces/ChartOptionsBase.md#overlaypricescales) option.
Alternatively, you can use [`IChartApi.priceScale`](/api/interfaces/IChartApi.md#pricescale) method to get an API object of any price scale or [`ISeriesApi.priceScale`](/api/interfaces/ISeriesApi.md#pricescale) to get an API object of series' price scale (the price scale that the series is attached to).
## Creating a price scale
By default a chart has only 2 price scales: `left` and `right`.
If you want to create an overlay price scale, you can simply assign [`priceScaleId`](/api/interfaces/SeriesOptionsCommon.md#pricescaleid) option to a series (note that a value should be differ from `left` and `right`) and a chart will automatically create an overlay price scale with provided ID.
If a price scale with such ID already exists then a series will be attached to this existing price scale.
Further you can use provided price scale ID to get its corresponding API object via [`IChartApi.priceScale`](/api/interfaces/IChartApi.md#pricescale) method.
## Removing a price scale
The default price scales (`left` and `right`) cannot be removed, you can only hide them by setting [`visible`](/api/interfaces/PriceScaleOptions.md#visible) option to `false`.
An overlay price scale exists while there is at least 1 series attached to this price scale.
Thus, to remove an overlay price scale remove all series attached to this price scale.
<!-- Note that this method is not implemented yet :(
## Equality of price scale API objects
`lightweight-charts` library does not guarantee to return the same reference of [`IPriceScaleApi`](/api/interfaces/IPriceScaleApi.md) object for the same price scale ID.
So you should never compare these objects by a reference, use the result from [`IPriceScaleApi.id`](/api/interfaces/IPriceScaleApi.md#id) method instead.
-->

View File

@ -0,0 +1,675 @@
---
title: Release Notes
description: List of changes made for each release of the library.
keywords:
- charts
- changelog
- canvas
- charting library
- charting
- html5 charts
- financial charting library
sidebar_position: 8
---
<!-- markdownlint-disable no-emphasis-as-heading -->
<!-- ^ using emphasis as headings so we don't have duplicate headers -->
## 4.2.0
**Enhancements**
- Added new [`attributionLogo`](https://tradingview.github.io/lightweight-charts/docs/api/interfaces/LayoutOptions#attributionLogo) option to `LayoutOptions`. This feature displays the TradingView attribution logo on the main chart pane by default, helping users meet the library's licensing requirements for attribution.
- The TradingView attribution logo can be easily hidden by setting the `attributionLogo` option to `false` in the chart's `layout` option.
- Improved data validation for `OhlcData` and `SingleValueData`. Introduced `isFulfilledBarData` for `OhlcData` and `isFulfilledLineData` for `SingleValueData`, ensuring more accurate validation of data types. Contributed by [@mozeryansky](https://github.com/mozeryansky) (PR [#1579](https://github.com/tradingview/lightweight-charts/pull/1579), fixes [#1526](https://github.com/tradingview/lightweight-charts/issues/1526)).
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.7..v4.2.0).
## 4.1.7
**Enhancements**
- Further Refinement of the Price Scale Label Alignment (PR [#1630](https://github.com/tradingview/lightweight-charts/pull/1630))
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.6..v4.1.7).
## 4.1.6
**Enhancements**
- Improved Price Scale Label Alignment: Enhanced the alignment algorithm for price scale labels to ensure they do not move out of the viewport. This improves the visibility of price labels, particularly when they are near the edges of the scale. Fixes [#1620](https://github.com/tradingview/lightweight-charts/issues/1620) (PR [#1621](https://github.com/tradingview/lightweight-charts/pull/1621))
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.5..v4.1.6).
## 4.1.5
**Enhancements**
- Added `IHorzScaleBehavior.shouldResetTickmarkLabels`. (PR [#1614](https://github.com/tradingview/lightweight-charts/pull/1614))
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.4..v4.1.5).
## 4.1.4
**Bug Fixes**
- Fixed hoveredSeries being undefined during series removal and creation. (PR [#1529](https://github.com/tradingview/lightweight-charts/pull/1529), fixes [#1406](https://github.com/tradingview/lightweight-charts/pull/1406), fixes [#1499](https://github.com/tradingview/lightweight-charts/pull/1499))
- Fixed price label rendering artefact. (PR [#1585](https://github.com/tradingview/lightweight-charts/pull/1585), fixes [#1584](https://github.com/tradingview/lightweight-charts/pull/1584))
- Fixed an issue that prevented primitives with `zOrder` set to `top` from drawing above the last price animation. (PR [#1576](https://github.com/tradingview/lightweight-charts/pull/1576))
- Fixed possible ReDos. (PR [#1536](https://github.com/tradingview/lightweight-charts/pull/1536))
- Fixed marker positioning, which could cause a space between histogram and bottom of the chart. (PR [#1538](https://github.com/tradingview/lightweight-charts/pull/1538) & [#1539](https://github.com/tradingview/lightweight-charts/pull/1539), fixes [#1382](https://github.com/tradingview/lightweight-charts/pull/1382))
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.3..v4.1.4).
## 4.1.3
**Minor Improvements**
- Added option to disable bold labels in the time scale. (PR [#1510](https://github.com/tradingview/lightweight-charts/pull/1510))
**Bug Fixes**
- Fixed sub-pixel horizontal alignment of the crosshair marker and series markers. (PR [#1505](https://github.com/tradingview/lightweight-charts/pull/1505), fixes [#1504](https://github.com/tradingview/lightweight-charts/issues/1504))
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.2..v4.1.3).
## 4.1.2
**Bug Fixes**
- Fix for 'Total canvas memory use exceeds the maximum limit' error raised on iOS Safari. (PR [#1485](https://github.com/tradingview/lightweight-charts/pull/1485))
**Minor Improvements**
- Improved error messages for price scale margins. (PR [#1489](https://github.com/tradingview/lightweight-charts/pull/1489))
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.1..v4.1.2).
## 4.1.1
**Bug Fixes**
- Fixed `shiftVisibleRangeOnNewBar` behaviour for realtime updates to a series. Additionally, a new option `allowShiftVisibleRangeOnWhitespaceReplacement` has been added if you wish to have the old 4.0 behaviour for when new data replaces existing whitespace. (PR [#1444](https://github.com/tradingview/lightweight-charts/pull/1444))
- When disabling touch scrolling on the chart via either the `vertTouchDrag` or `horzTouchDrag` setting in the `handleScroll` options, any touch scroll events over the corresponding scale will now be ignored so the page can be scrolled. (PR [#1445](https://github.com/tradingview/lightweight-charts/pull/1445))
[Changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.1.0..v4.1.1).
## 4.1.0
Version 4.1 of Lightweight Charts introduces exciting new features, including the introduction of Plugins, which provide developers the ability to extend the library's functionality. Additionally, this release includes enhancements to customize the horizontal scale and various minor improvements and bug fixes.
**Major Updates**
**Plugins**
Developers can now leverage the power of Plugins in Lightweight Charts. Two types of Plugins are supported -  [Custom Series](/plugins/intro.md#custom-series) and [Drawing Primitives](/plugins/intro.md#drawing-primitives), offering the ability to define new series types and create custom visualizations, drawing tools, and annotations.
With the flexibility provided by these plugins, developers can create highly customizable charting applications for their users.
To get started with plugins, please refer to our [Plugins Documentation](/plugins/intro.md) for a better understanding of what is possible and how plugins work. You can also explore our collection of [plugin examples](https://github.com/tradingview/lightweight-charts/tree/master/plugin-examples) (with a [preview hosted here](https://tradingview.github.io/lightweight-charts/plugin-examples/)) for inspiration and guidance on implementing specific functionality.
To help you get started quickly, we have created an NPM package called [create-lwc-plugin](https://www.npmjs.com/package/create-lwc-plugin), which sets up a plugin project for you. This way, you can hit the ground running with your plugin development.
**Horizontal Scale Customization**
The horizontal scale is no longer restricted to only time-based values. The API has been extended to allow customization of the horizontal scale behavior, and enable uses cases like options chart where price values are displayed in the horizontal scale. The most common use-case would be to customise the tick marks behaviour.
The [createChartEx](/api/index.md#createchartex) function should be used instead of the usual `createChart` function, and an instance of a class implementing [IHorzScaleBehavior](/api/interfaces/IHorzScaleBehavior.md) should be provided.
A simple example can be found in this test case: [horizontal-price-scale.js](https://github.com/tradingview/lightweight-charts/blob/master/tests/e2e/graphics/test-cases/horizontal-price-scale.js)
**Enhancements**
- Added point markers styling option for line-based series. (closes [#365](https://github.com/tradingview/lightweight-charts/issues/365)) [Docs](/api/interfaces/LineStyleOptions.md#pointmarkersvisible)
- Added double click subscriber for the main chart pane. (closes [#1385](https://github.com/tradingview/lightweight-charts/issues/1385)) [Docs](/api/interfaces/IChartApi.md#subscribedblclick)
- Added `setCrosshairPosition` API, allowing programmatic setting of the crosshair position. (fixes [#1198](https://github.com/tradingview/lightweight-charts/issues/1198), [#1163](https://github.com/tradingview/lightweight-charts/issues/1163), [#438](https://github.com/tradingview/lightweight-charts/issues/438)) [Docs](/api/interfaces/IChartApi.md#setCrosshairPosition)
- Added an option to disable crosshair. Introduced the `Hidden` option in the `CrosshairMode` setting. (closes [#749](https://github.com/tradingview/lightweight-charts/issues/749), thanks to [@luk707](https://github.com/luk707))
- Allow overriding tick mark label length via the `tickMarkMaxCharacterLength` option. (closes [#1396](https://github.com/tradingview/lightweight-charts/issues/1396)) [Docs](/api/interfaces/HorzScaleOptions.md#tickmarkmaxcharacterlength)
- Support for overriding the percentage formatter within the localization options. (fixes [#1328](https://github.com/tradingview/lightweight-charts/issues/1328), [#1291](https://github.com/tradingview/lightweight-charts/issues/1291)) [Docs](/api/interfaces/LocalizationOptions.md#percentageformatter)
- Added `paneSize` getter to `IChartApi`, returning the dimensions of the chart pane. (issue [#1411](https://github.com/tradingview/lightweight-charts/issues/1411)) [Docs](/api/interfaces/IChartApi.md#panesize)
- Added options to set minimum dimensions for the price and time scales. (closes [#1062](https://github.com/tradingview/lightweight-charts/issues/1062), related to [#1163](https://github.com/tradingview/lightweight-charts/issues/1163), [#50](https://github.com/tradingview/lightweight-charts/issues/50)) [Docs](/api/interfaces/TimeScaleOptions.md#minimumheight), [Docs](/api/interfaces/PriceScaleOptions.md#minimumwidth)
**Bug Fixes**
- Fixed chart layout when direction is set to RTL. (PR [#1338](https://github.com/tradingview/lightweight-charts/pull/1338))
- Fixed re-enabling of `autoSize` after disabling it. (PR [#1274](https://github.com/tradingview/lightweight-charts/pull/1377))
- Corrected percentage mode and zero first value. (fixes [#1386](https://github.com/tradingview/lightweight-charts/issues/1386))
- Prevent chart shifting when new data replaces existing whitespace. (fixes [#1201](https://github.com/tradingview/lightweight-charts/issues/1201))
Thanks to our Contributors for this Release:
- [@luk707](https://github.com/luk707)
You can always send us your feedback via GitHub.
We look forward to hearing from you! And as always, happy trading!
Team TradingView
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/24?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.0.1..v4.1.0).
## 4.0.1
**Enhancements**
- Add the ability to specify font colour for the Priceline labels. [#1274](https://github.com/tradingview/lightweight-charts/issues/1274) [#1287](https://github.com/tradingview/lightweight-charts/issues/1287)
- Ignore resize method if `autoSize` is active, and added API to check if active. [#1301](https://github.com/tradingview/lightweight-charts/issues/1301)
**Bug fixes**
- Typo in customization guide. Thanks [@UcheAzubuko](https://github.com/UcheAzubuko). [#1284](https://github.com/tradingview/lightweight-charts/issues/1284)
- Inability to immediately add markers when `autoSize` chart option is enabled. Thanks [@victorbrambati](https://github.com/victorbrambati). [#1271](https://github.com/tradingview/lightweight-charts/issues/1271) [#1281](https://github.com/tradingview/lightweight-charts/issues/1281)
- First render when using `autosize` doesn't show the latest bars. Thanks [@victorbrambati](https://github.com/victorbrambati) [#1281](https://github.com/tradingview/lightweight-charts/issues/1281). [#1282](https://github.com/tradingview/lightweight-charts/issues/1282)
- Series rendering bug when outside of visible range. [#1293](https://github.com/tradingview/lightweight-charts/issues/1293) [#1294](https://github.com/tradingview/lightweight-charts/issues/1294)
- Auto contrast text color for crosshair labels. [#1309](https://github.com/tradingview/lightweight-charts/issues/1309) [#1310](https://github.com/tradingview/lightweight-charts/issues/1310)
- Hit box from the text of marker incorrectly shifted to the right. [#1270](https://github.com/tradingview/lightweight-charts/issues/1270) [#1305](https://github.com/tradingview/lightweight-charts/issues/1305)
As always, we thank you for your support and help in making Lightweight Charts™ the best product on the financial web. And a big shout out to our hero contributors [@victorbrambati](https://github.com/victorbrambati), and [@UcheAzubuko](https://github.com/UcheAzubuko)!
You can always send us your feedback via GitHub.
We look forward to hearing from you! And as always, happy trading!
Team TradingView
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/25?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v4.0.0..v4.0.1).
## 4.0.0
Long overdue as its been nearly 1 year since our last major update, but behold before all the changes that have happened over the last 12 months.
In total, more than 20 tickets have been addressed with one of the most important ones being **fancy-canvas** the library we use to configure HTML canvas in Lightweight Charts™.
Please view the migration guide here: [Migrating from v3 to v4](./migrations/from-v3-to-v4).
**Breaking changes**
- Fancy-canvas 2 | [#818](https://github.com/tradingview/lightweight-charts/issues/818)
- Added support for ES module exports | [#613](https://github.com/tradingview/lightweight-charts/issues/613)
- We are now generating two more build types: esm, standalone & cjs
- Updated scales design | [#606](https://github.com/tradingview/lightweight-charts/issues/606)
- Changed scales look & feel according to the new design
- Add possibility to disable time axis ticks | [#1043](https://github.com/tradingview/lightweight-charts/issues/1043)
- Added `ticksVisible` to `TimeScaleOptions`, renamed `drawTicks` to `ticksVisible` in `PriceScaleOptions`.
- Custom price lines should be atop of the series | [#684](https://github.com/tradingview/lightweight-charts/issues/684)
- Price line to be added on top of any series - shout out to thanhlmm
- Remove deprecated code | [#626](https://github.com/tradingview/lightweight-charts/issues/626)
- Fix types inconsistency on API level with time | [#470](https://github.com/tradingview/lightweight-charts/issues/470)
- Add API to get chart values (data, markers, etc) | [#414](https://github.com/tradingview/lightweight-charts/issues/414)
- Added methods:
- `ISeriesApi.markers`
- `ISeriesApi.dataByIndex`
- Changed time types everywhere in the public API to `Time`
**Enhancements**
- Handle resize with ResizeObserver if it's exist in window | [#71](https://github.com/tradingview/lightweight-charts/issues/71)
- There was an issue when resizing the chart (such as rotating the screen of a phone/tablet).
- Add possibility to use default tick mark formatter implementation as a fallback | [#1210](https://github.com/tradingview/lightweight-charts/issues/1210)
- Allow the custom tick mark formatter to return null so that it will use the default formatter for that specific mark.
- Add possibility to invert Area series filled area | [#1115](https://github.com/tradingview/lightweight-charts/issues/1115)
- Adds invertFilledArea property to the AreaStyleOptions which when set to true will invert the filled area (draw above the line instead of below it).
- Documentation website improvements | [#1001](https://github.com/tradingview/lightweight-charts/issues/1001) [#1002](https://github.com/tradingview/lightweight-charts/issues/1002)
- Provides a way to apply theme-based colors to a chart whenever the Docusaurus theme is changed.
- Add ability to draw parts of area with different colors | [#1100](https://github.com/tradingview/lightweight-charts/issues/1100)
- Add a possibility to change line, top and bottom colors for the different parts of an area series
- Add possibility to change price axis text color | [#1114](https://github.com/tradingview/lightweight-charts/issues/1114)
- Reset price and time scale double click options | [#1118](https://github.com/tradingview/lightweight-charts/issues/1118)
- Distinguishing the reset between price & time scale vs having only one option
- Add curved lines | [#506](https://github.com/tradingview/lightweight-charts/issues/506)
- Add a new line type that draws curved lines between series points.
**Chores**
- Replace deprecated String.prototype.substr | [#1048](https://github.com/tradingview/lightweight-charts/issues/1048)
- Shout out to CommanderRoot
**Bug fixes**
- Refactoring resize the chart | [#367](https://github.com/tradingview/lightweight-charts/issues/367)
- The chart is blank on printed page in Chromium | [#873](https://github.com/tradingview/lightweight-charts/issues/873)
- Chart was blank when printing
- Horizontal scroll animations improvements | [#1136](https://github.com/tradingview/lightweight-charts/issues/1136)
- Fixes glitches when resetting the chart time scale while scrolling
- Draw series last price & price line labels on the top layer | [#1046](https://github.com/tradingview/lightweight-charts/issues/1046)
- Fixes an issue where price line could be place over price scale labels
- Incorrect price line labels formatting | [#1032](https://github.com/tradingview/lightweight-charts/issues/1032)
- When setting the price scale mode to anything than 'Normal' the price for PriceLine wasn't properly calculated.
- lockVisibleTimeRangeOnResize does not work with fixLeftEdge | [#991](https://github.com/tradingview/lightweight-charts/issues/991)
- The visible range is no longer changed after resizing the chart.
- Crosshair label text appears on the chart during initial render | [#1255](https://github.com/tradingview/lightweight-charts/issues/1255)
- Small text artefacts from the crosshair no longer appear on the time axis before any interaction with the chart.
As always, we thank you for your support and help in making Lightweight Charts™ the best product on the financial web. And a big shout out to our hero contributors [thanhlmm](https://github.com/thanhlmm), [CommanderRoot](https://github.com/CommanderRoot), [samhainsamhainsamhain](https://github.com/samhainsamhainsamhain) & colleague [Nipheris](https://github.com/Nipheris)!
You can always send us your feedback via GitHub.
We look forward to hearing from you! And as always, happy trading!
Team TradingView
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/18?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.8.0..v4.0.0).
## 3.8.0
We're happy to announce the next release of Lightweight Charts™ library. This release includes many improvements and bug fixes (as usual), but we are thrilled to say that from this version the library has its own [documentation website](https://tradingview.github.io/lightweight-charts/) that replaces the documentation in the repository. Check it out and share your feedback in [this discussion thread](https://github.com/tradingview/lightweight-charts/discussions/921).
**Enhancement**
- Documentation website (see [#875](https://github.com/tradingview/lightweight-charts/issues/875), [#918](https://github.com/tradingview/lightweight-charts/issues/918), [#411](https://github.com/tradingview/lightweight-charts/issues/411), [#919](https://github.com/tradingview/lightweight-charts/issues/919), [#922](https://github.com/tradingview/lightweight-charts/issues/922), [#983](https://github.com/tradingview/lightweight-charts/issues/983), [#980](https://github.com/tradingview/lightweight-charts/issues/980), [#1006](https://github.com/tradingview/lightweight-charts/issues/1006))
- Quick tracking mode (see [#830](https://github.com/tradingview/lightweight-charts/issues/830))
- Improved mouse behaviour on touch devices (like mouse connected to mobile phone/tablet) (see [#106](https://github.com/tradingview/lightweight-charts/issues/106))
- Custom color for items of candlestick and line series (see [#195](https://github.com/tradingview/lightweight-charts/issues/195))
- Labels might be cut off when disabling scale and scroll ([#947](https://github.com/tradingview/lightweight-charts/issues/947))
- Add ability to disable visibility of price line line (see [#969](https://github.com/tradingview/lightweight-charts/issues/969))
**Fixed**
- timeScale.fitContent is not working correctly (see [#966](https://github.com/tradingview/lightweight-charts/issues/966))
- Delegate.unsubscribeAll method works in opposite way (see [#995](https://github.com/tradingview/lightweight-charts/issues/995))
- Last price animation is active when no data added to the right (but to the left) (see [#886](https://github.com/tradingview/lightweight-charts/issues/886))
- subscribeClick on mobile always get the last index of all the items (see [#657](https://github.com/tradingview/lightweight-charts/issues/657))
- Incorrect crosshair position on scrolling by dragging by mouse (see [#987](https://github.com/tradingview/lightweight-charts/issues/987))
- A painting slows down after a while with tons of updates (see [#946](https://github.com/tradingview/lightweight-charts/issues/946))
- Bars disappear with devicePixelRatio less than 1 (see [#982](https://github.com/tradingview/lightweight-charts/issues/982))
- There are no tick marks on the price axis (see [#939](https://github.com/tradingview/lightweight-charts/issues/939))
- Disabling scrolling by disabled horzTouchDrag and vertTouchDrag options disables moving crosshair in tracking mode (see [#434](https://github.com/tradingview/lightweight-charts/issues/434))
- Reducing precision doesn't update price scale width (see [#550](https://github.com/tradingview/lightweight-charts/issues/550))
- Chart width is jumping on series change from area to candles (see [#943](https://github.com/tradingview/lightweight-charts/issues/943))
- Log axis is not scaling on small number (see [#874](https://github.com/tradingview/lightweight-charts/issues/874))
- Overlay series title is not rendered when no series use right price scale (see [#926](https://github.com/tradingview/lightweight-charts/issues/926))
- scrollToPosition with big negative value and when no data breaks the chart (see [#889](https://github.com/tradingview/lightweight-charts/issues/889))
- When rendering multiple charts with baseline series, baseValue of the last element is used on all charts series. (see [#898](https://github.com/tradingview/lightweight-charts/issues/898))
Thanks to our contributors:
- [@zaleGZL](https://github.com/zaleGZL) zale
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/23?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.7.0..v3.8.0).
## 3.7.0
**Enhancement**
- The new baseline series chart (see [#151](https://github.com/tradingview/lightweight-charts/issues/151))
- Documentation about time zones support (see [#781](https://github.com/tradingview/lightweight-charts/issues/781))
- Added methods to get time axis size and subscribe on size change (see [#853](https://github.com/tradingview/lightweight-charts/issues/853))
- Improved performance of setting/updating series data (see [#418](https://github.com/tradingview/lightweight-charts/issues/418) and [#838](https://github.com/tradingview/lightweight-charts/issues/838))
- Use lowerbound in TimeScale timeToIndex search (see [#767](https://github.com/tradingview/lightweight-charts/issues/767))
- Add JSDoc comments for existing API docs (see [#870](https://github.com/tradingview/lightweight-charts/issues/870))
**Fixed**
- Increased min price tick mark step up to 1e-14 (from 1e-9) (see [#841](https://github.com/tradingview/lightweight-charts/issues/841))
- Fix typo in customization docs (see [#844](https://github.com/tradingview/lightweight-charts/issues/844))
- Do not paint time axis if it not visible (see [#865](https://github.com/tradingview/lightweight-charts/issues/865))
- Remove color customisation from settings.json (see [#869](https://github.com/tradingview/lightweight-charts/issues/869))
Thanks to our contributors:
- [@thanhlmm](https://github.com/thanhlmm) Thanh Le
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/22?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.6.1..v3.7.0).
## 3.6.1
**Fixed**
- In v3.6.0 there was a typo in `LasPriceAnimationMode` const enum (`Last` without `t`), which was fixed in this release. The incorrect name is still available to import and could be used in your code so no breaking change so far (see e5133cb0c50fc557182aba4011e170aaf30a5b1a)
See [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.6.0..v3.6.1).
## 3.6.0
On this day 10 years ago, 10th September 2011, the very first version of the TradingView website was deployed. To celebrate 10th anniversary we're happy to announce the new version of lightweight-charts library v3.6.0 🎉🎉🎉
**Enhancement**
- Gradient chart background color (see [#831](https://github.com/tradingview/lightweight-charts/issues/831))
- How to add buffer animation to price jump (see [#567](https://github.com/tradingview/lightweight-charts/issues/567))
- Kinetic scroll (see [#832](https://github.com/tradingview/lightweight-charts/issues/832))
**Fixed**
- Incorrect initial barSpacing when both fixRightEdge and fixLeftEdge are enabled (see [#823](https://github.com/tradingview/lightweight-charts/issues/823))
- Time axis label get cut on the edge if a fix edge option is enabled (see [#835](https://github.com/tradingview/lightweight-charts/issues/835))
- Price axis doesn't respect the width of crosshair label (see [#834](https://github.com/tradingview/lightweight-charts/issues/834))
- Fixed both timescale edges make lockVisibleTimeRangeOnResize turn wrong (see [#814](https://github.com/tradingview/lightweight-charts/issues/814))
- `Error: Value is null` error while set the data is container has 0x0 size (see [#821](https://github.com/tradingview/lightweight-charts/issues/821))
Thanks to our contributors:
- [@thanhlmm](https://github.com/thanhlmm) Thanh Le
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/21?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.5.0..v3.6.0).
## 3.5.0
**A note about rendering order of series, which might be interpret as a bug or breaking change since this release**
This is not really a breaking change, but might be interpret like that. In [#794](https://github.com/tradingview/lightweight-charts/issues/794) we've fixed the wrong order of series, thus now all series will be displayed in opposite order (they will be displayed in order of creating now; previously they were displayed in reversed order).
To fix that, just change the order of creating the series (thus instead of create series A, then series B create series B first and then series A) - see [#812](https://github.com/tradingview/lightweight-charts/issues/812).
**Fixed**
- Screenshot output missing piece on bottom right (see [#798](https://github.com/tradingview/lightweight-charts/issues/798))
- Overlapped line chart show wrong color order when hover (see [#794](https://github.com/tradingview/lightweight-charts/issues/794))
- Price line label show on both axis (see [#795](https://github.com/tradingview/lightweight-charts/issues/795))
Thanks to our contributors:
- [@thanhlmm](https://github.com/thanhlmm) Thanh Le
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/20?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.4.0..v3.5.0).
## 3.4.0
**Enhancement**
- Add option to fix right edge (see [#218](https://github.com/tradingview/lightweight-charts/issues/218))
- Drop restriction for min bar spacing value (see [#558](https://github.com/tradingview/lightweight-charts/issues/558))
- Round corners of the line-style plots (see [#731](https://github.com/tradingview/lightweight-charts/issues/731))
**Fixed**
- AutoscaleProvider documentation error (see [#773](https://github.com/tradingview/lightweight-charts/issues/773))
- Candlestick upColor and downColor is not changed on applyOptions (see [#750](https://github.com/tradingview/lightweight-charts/issues/750))
- Cleared and reset data appears at visually different location (see [#757](https://github.com/tradingview/lightweight-charts/issues/757))
- Remove unused internal method from SeriesApi (see [#768](https://github.com/tradingview/lightweight-charts/issues/768))
- Removing data for the last series doesn't actually remove the data (see [#752](https://github.com/tradingview/lightweight-charts/issues/752))
- `to` date of getVisibleRange contains partially visible data item and it's impossible to hover it (see [#624](https://github.com/tradingview/lightweight-charts/issues/624))
- series.priceFormatter().format(price) does not work (see [#790](https://github.com/tradingview/lightweight-charts/issues/790))
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/19?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.3.0..v3.4.0).
## 3.3.0
**Enhancement**
- Add type predicates for series type (see [#670](https://github.com/tradingview/lightweight-charts/issues/670))
- Create Grid instance for every pane (see [#382](https://github.com/tradingview/lightweight-charts/issues/382))
- Add possibility to chose crosshairMarker color, so it will be independent from line-series color (see [#310](https://github.com/tradingview/lightweight-charts/issues/310))
- Implement option not to shift the time scale at all when data is added with `setData` (see [#584](https://github.com/tradingview/lightweight-charts/issues/584))
**Fixed**
- Incorrect bar height when its value is more than chart's height (see [#673](https://github.com/tradingview/lightweight-charts/issues/673))
- Disabling autoScale for non-visible series (see [#687](https://github.com/tradingview/lightweight-charts/issues/687))
Thanks to our contributors:
- [@dubroff](https://github.com/dubroff)
- [@SuperPenguin](https://github.com/SuperPenguin) Andree Yosua
- [@mecm1993](https://github.com/mecm1993) Manuel Cepeda
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/17?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.2.0..v3.3.0).
## 3.2.0
**Enhancement**
- Feat/gzip friendly colors (see [#598](https://github.com/tradingview/lightweight-charts/issues/598))
- Add coordinateToLogical and logicalToCoordinate (see [#587](https://github.com/tradingview/lightweight-charts/issues/587))
- Add API to show/hide series without removing it (see [#471](https://github.com/tradingview/lightweight-charts/issues/471))
- Add run-time validation of inputs in debug mode (see [#315](https://github.com/tradingview/lightweight-charts/issues/315))
- Pixel perfect renderers fixes (see [#535](https://github.com/tradingview/lightweight-charts/issues/535))
- Add title option to createPriceLine (see [#357](https://github.com/tradingview/lightweight-charts/issues/357))
**Fixed**
- Set rightOffset and scrollToPosition async as well as setVisibleRange (see [#406](https://github.com/tradingview/lightweight-charts/issues/406))
- timeScale() changes visible range on setData() (see [#549](https://github.com/tradingview/lightweight-charts/issues/549))
- Remove chart's size restriction or make it smaller (see [#366](https://github.com/tradingview/lightweight-charts/issues/366))
- LineStyle.Dotted make no effect (see [#572](https://github.com/tradingview/lightweight-charts/issues/572))
- If priceScaleId is empty string, invalid price scale api is returned (see [#537](https://github.com/tradingview/lightweight-charts/issues/537))
- Incorrect Selection seen on long press in ios webview on chart (see [#609](https://github.com/tradingview/lightweight-charts/issues/609))
- One-point line series is invisible (see [#597](https://github.com/tradingview/lightweight-charts/issues/597))
- Empty price scale after creating series with the same price range (see [#615](https://github.com/tradingview/lightweight-charts/issues/615))
**Infra and dev env**
- Compress artifacts in graphics tests in CI (see [#145](https://github.com/tradingview/lightweight-charts/issues/145))
- Run tests against production build (see [#503](https://github.com/tradingview/lightweight-charts/issues/503))
- Add test to check code usage coverage (see [#495](https://github.com/tradingview/lightweight-charts/issues/495))
- Migrate from codechecks (see [#356](https://github.com/tradingview/lightweight-charts/issues/356))
- Updated dev deps
Thanks to our contributors:
- Andree Yosua [@SuperPenguin](https://github.com/SuperPenguin)
- Christos [@christose](https://github.com/christose)
- Shergin Rodion [@beholderrk](https://github.com/beholderrk)
- wenhoujx [@wenhoujx](https://github.com/wenhoujx)
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/11?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.1.5..v3.2.0).
## 3.1.5
It's a just re-published accidentally published 3.1.4 version, which didn't actually fix the issue [#536](https://github.com/tradingview/lightweight-charts/issues/536).
Version 3.1.4 has been deprecated.
**Fixed**
- TypeError `_internal_priceScale is not a function` while getting series price scale (see [#536](https://github.com/tradingview/lightweight-charts/issues/536))
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/16?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.1.3..v3.1.5).
## 3.1.3
**Fixed**
- `handleScroll` and `handleScale` options aren't applied (see [#527](https://github.com/tradingview/lightweight-charts/issues/527))
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/14?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.1.2..v3.1.3).
## 3.1.2
**Fixed**
- Crosshair doesn't work on touch devices (see [#511](https://github.com/tradingview/lightweight-charts/issues/511))
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/13?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.1.1..v3.1.2).
## 3.1.1
**Fixed**
- Fixed production build of 3.1 version (see [#502](https://github.com/tradingview/lightweight-charts/issues/502) and [62aa93724e40fbb1b678d9b44655279a1df529c5](https://github.com/tradingview/lightweight-charts/commit/62aa93724e40fbb1b678d9b44655279a1df529c5))
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/12?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.1.0..v3.1.1).
## 3.1.0
**Enhancement**
- Whitespaces support (see [#209](https://github.com/tradingview/lightweight-charts/issues/209))
- Custom font families for watermarks (see [#437](https://github.com/tradingview/lightweight-charts/issues/437))
**Fixed**
- Added support for 'transparent' color (see [#491](https://github.com/tradingview/lightweight-charts/issues/491))
- Refactor DataLayer/ChartApi (see [#270](https://github.com/tradingview/lightweight-charts/issues/270))
- Remove series then scroll to right after not working (see [#355](https://github.com/tradingview/lightweight-charts/issues/355))
- Scaling via mouse click and drag doesn't work if chart is inside shadow root (see [#427](https://github.com/tradingview/lightweight-charts/issues/427))
- Applying watermark in setTimeout doesn't make an effect (see [#485](https://github.com/tradingview/lightweight-charts/issues/485))
- Importing the library in server-side context caused `ReferenceError` (see [#446](https://github.com/tradingview/lightweight-charts/issues/446))
**Undocumented breaking changes**
We know that some of users probably used some hacky-workarounds calling internal methods to achieve multi-pane support. In this release, to reduce size of the bundle we [dropped out a code for pane's separator](https://github.com/tradingview/lightweight-charts/pull/496) (which allows to resize panes).
As soon this workaround is undocumented and we don't support this feature yet - we don't bump a major version. But we think it's better to let you know that it has been changed.
**Development**
- Dropped support NodeJS < 12.18
- Migrated from TSLint to ESLint (see [#314](https://github.com/tradingview/lightweight-charts/issues/314))
- Migrated from clean-publish to in-house script to clear package.json (see [#474](https://github.com/tradingview/lightweight-charts/issues/474))
Thanks to our contributors:
- Meet Mangukiya [@meetmangukiya](https://github.com/meetmangukiya)
- NekitCorp [@NekitCorp](https://github.com/NekitCorp)
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/9?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.0.1..v3.1.0).
## 3.0.1
**Fixed**
- Correctly handle `overlay: true` in series options while create series to backward compat (see [#475](https://github.com/tradingview/lightweight-charts/issues/475))
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/10?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v3.0.0..v3.0.1).
## 3.0.0
**Breaking changes**
We have some breaking changes since the latest version due some features and API improvements:
- Methods `subscribeVisibleTimeRangeChange` and `unsubscribeVisibleTimeRangeChange` has been moved from ChartApi to TimeScaleApi
- Since 3.0 you can specify price axis you'd like to place the series on. The same for moving the series between price scales (see migration guide below)
See [breaking changes doc](https://github.com/tradingview/lightweight-charts/blob/master/docs/3.0-breaking-changes.md) with migration guide to migrate smoothly.
**Enhancement**
- Added ability to customize time scale tick marks formatter (see [#226](https://github.com/tradingview/lightweight-charts/issues/226))
- Added ability to put text for series markers (see [#207](https://github.com/tradingview/lightweight-charts/issues/207))
- Added ability to specify your own date formatter (see [#368](https://github.com/tradingview/lightweight-charts/issues/368))
- Improved tick marks generation algorithm for the first point (see [#387](https://github.com/tradingview/lightweight-charts/issues/387))
- Made inbound types weakly (outbound ones should be strict) (see [#374](https://github.com/tradingview/lightweight-charts/issues/374))
- Removed non-exported const enum's JS code (see [#432](https://github.com/tradingview/lightweight-charts/issues/432))
- Introduced [ts-transformer-properties-rename](https://github.com/timocov/ts-transformer-properties-rename) instead of [ts-transformer-minify-privates](https://github.com/timocov/ts-transformer-minify-privates) (see [#436](https://github.com/tradingview/lightweight-charts/issues/436))
**Added**
- Add ability to override series' autoscale range (see [#392](https://github.com/tradingview/lightweight-charts/issues/392))
- Add API to get price scale's width (see [#452](https://github.com/tradingview/lightweight-charts/issues/452))
- Disabling/enabling scaling axis for both price and time (see [#440](https://github.com/tradingview/lightweight-charts/issues/440))
- Get screen coordinate by a time point (see [#435](https://github.com/tradingview/lightweight-charts/issues/435))
- Remove tick mark from price label (see [#378](https://github.com/tradingview/lightweight-charts/issues/378))
- Support the second price axis (see [#129](https://github.com/tradingview/lightweight-charts/issues/129))
- Visible time range should have bars count of the space from left/right (see [#335](https://github.com/tradingview/lightweight-charts/issues/335))
**Fixed**
- `series.setMarkers` requires at least one data point (see [#372](https://github.com/tradingview/lightweight-charts/issues/372))
- Impossible to override the only width or height in constructor (see [#353](https://github.com/tradingview/lightweight-charts/issues/353))
- Incorrect alignment of markers if series has gaps (see [#464](https://github.com/tradingview/lightweight-charts/issues/464))
- Multiple series: error while trying to scroll the chart (see [#373](https://github.com/tradingview/lightweight-charts/issues/373))
- Replace const enums with enums to let use them in projects with enabled isolatedModules option (see [#375](https://github.com/tradingview/lightweight-charts/issues/375))
Thanks to our contributors:
- Ben Guidarelli [@barnjamin](https://github.com/barnjamin)
- gkaindl [@gkaindl](https://github.com/gkaindl)
- scrwdrv [@scrwdrv](https://github.com/scrwdrv)
- Yusuf Sahin HAMZA [@yusufsahinhamza](https://github.com/yusufsahinhamza)
See [issues assigned to this version's milestone](https://github.com/tradingview/lightweight-charts/milestone/7?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v2.0.0..v3.0.0).
## 2.0.0
**Breaking changes**
- Removed unused `lineWidth` property from `HistogramStyleOptions` interface (it affects nothing, but could break your compilation)
- Changed order of `width` and `height` args in `resize` method ([#157](https://github.com/tradingview/lightweight-charts/issues/157))
- Pattern for all non-solid (dotted, dashed, large dashed and sparse dotted) line styles was a bit changed ([#274](https://github.com/tradingview/lightweight-charts/issues/274))
**Enhancement**
- Pixel-perfect rendering ([#274](https://github.com/tradingview/lightweight-charts/issues/274))
- Time scale enhancements ([#352](https://github.com/tradingview/lightweight-charts/issues/352))
**Added**
- Disable all kinds of scrolls and touch with one option ([#230](https://github.com/tradingview/lightweight-charts/issues/230))
- Added to the acceptable date formats ([#296](https://github.com/tradingview/lightweight-charts/issues/296))
- Add option to show the "global" last value of the series instead of the last visible ([#203](https://github.com/tradingview/lightweight-charts/issues/203))
**Fixed**
- Price line didn`t hightlight price ([#273](https://github.com/tradingview/lightweight-charts/issues/273))
- CreatePriceLine not removed ([#285](https://github.com/tradingview/lightweight-charts/issues/285))
- Crosshair line not visible when priceScale position set to none ([#302](https://github.com/tradingview/lightweight-charts/issues/302))
- chart.resize parameter is inverted ([#157](https://github.com/tradingview/lightweight-charts/issues/157))
- Removed unnecessary spacing from left/right (1 bar from each side) in `fitContent` ([#345](https://github.com/tradingview/lightweight-charts/issues/345))
Thanks to our contributors:
- Andree Yosua [@SuperPenguin](https://github.com/SuperPenguin)
- kpaape [@kpaape](https://github.com/kpaape)
- Matt Conway [@RetWolf](https://github.com/RetWolf)
See [issues assigned to this versions milestone](https://github.com/tradingview/lightweight-charts/milestone/6?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v1.2.2..v2.0.0).
## 1.2.2
**Fixed**
- Bug while rendering few datasets with not equal timescale ([#321](https://github.com/tradingview/lightweight-charts/issues/321))
## 1.2.1
**Added**
- Add custom price lines ([#183](https://github.com/tradingview/lightweight-charts/issues/183))
- Migrate canvas-related logic to fancy-canvas library ([#141](https://github.com/tradingview/lightweight-charts/issues/141))
- Add coordinateToPrice method to ISeriesApi ([#171](https://github.com/tradingview/lightweight-charts/issues/171))
**Fixed**
- Scrolling by price is incorrect ([#213](https://github.com/tradingview/lightweight-charts/issues/213))
- Histogram (volume) does not honor color setting (sometimes) ([#233](https://github.com/tradingview/lightweight-charts/issues/233))
- Logarithmic scaling is applied to volume ([#227](https://github.com/tradingview/lightweight-charts/issues/227))
- hoveredSeries in mouse events params is always undefined ([#190](https://github.com/tradingview/lightweight-charts/issues/190))
- `lineType` option does not work for area/line series ([#220](https://github.com/tradingview/lightweight-charts/issues/220))
- Double clicking on time scale will reset fix left edge ([#224](https://github.com/tradingview/lightweight-charts/issues/224))
- Series' marker does not aligned after autoscale ([#212](https://github.com/tradingview/lightweight-charts/issues/212))
- Error on setData empty array for overlay histogram series ([#267](https://github.com/tradingview/lightweight-charts/issues/267))
- Added some missing docs ([#211](https://github.com/tradingview/lightweight-charts/issues/211) [#193](https://github.com/tradingview/lightweight-charts/issues/193) [#245](https://github.com/tradingview/lightweight-charts/issues/245))
See [issues assigned to this versions milestone](https://github.com/tradingview/lightweight-charts/milestone/4?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v1.1.0...v1.2.1).
## 1.1.0
**Added**
- Apply localization to specific series ([#62](https://github.com/tradingview/lightweight-charts/issues/62))
- Series-based markers ([#24](https://github.com/tradingview/lightweight-charts/issues/24))
- Reduced size of the library by using [`ts-transformer-minify-privates`](https://github.com/timocov/ts-transformer-minify-privates) transformer ([#98](https://github.com/tradingview/lightweight-charts/issues/98))
**Fixed**
- The chart can't start from the left ([#144](https://github.com/tradingview/lightweight-charts/issues/144))
- OHLC charts render incorrect when `value` is provided ([#165](https://github.com/tradingview/lightweight-charts/issues/165))
- Price axis is not shown if series is created inside promise chain ([#164](https://github.com/tradingview/lightweight-charts/issues/164))
- The line chart can't move to the left ([#143](https://github.com/tradingview/lightweight-charts/issues/143))
- Lots of non-passive event listener warnings ([#139](https://github.com/tradingview/lightweight-charts/issues/139))
- applyOptions of histogram series with color doesn't affect the data ([#112](https://github.com/tradingview/lightweight-charts/issues/112))
- Price Axis Scaling Bug ([#122](https://github.com/tradingview/lightweight-charts/issues/122))
- LineSeries is not displayed if starting x value is out of viewport ([#116](https://github.com/tradingview/lightweight-charts/issues/116))
- Crosshair isn't updated when timescale is changed ([#120](https://github.com/tradingview/lightweight-charts/issues/120))
- Pinch isn't prevented by long tap ([#95](https://github.com/tradingview/lightweight-charts/issues/95))
Thanks to our contributors:
- zach [@n8tb1t](https://github.com/n8tb1t)
- Chris Kaczor [@krzkaczor](https://github.com/krzkaczor)
See [issues assigned to this versions milestone](https://github.com/tradingview/lightweight-charts/milestone/2?closed=1) or [changes since the last published version](https://github.com/tradingview/lightweight-charts/compare/v1.0.2...v1.1.0).
## 1.0.2
**Fixed**
- The histogram last bar not hide in chart ([#133](https://github.com/tradingview/lightweight-charts/issues/133))
## 1.0.1
**Fixed**
- Setting the data to series fails after setting the data to histogram series with custom color ([#110](https://github.com/tradingview/lightweight-charts/issues/110))
## 1.0.0
The first release.
The docs for this version are available [here](https://github.com/tradingview/lightweight-charts/tree/v1.0.0/docs).

View File

@ -0,0 +1,168 @@
---
sidebar_position: 1
---
# Series types
In this article you can read a brief overview of all supported series types.
## Series Customisation
Customization options for series are dependent on their specific type. Each type of series has its own set of available options, which can be found in the documentation provided for that particular series type. This means that any type of series can be customized, but the options you can apply will vary depending on the type of series you are working with.
If you'd like to change any option of a series, you could do this in different ways:
1. You can specify the default options while creating a series:
```js
// change default top & bottom colors of an area series in creating time
const series = chart.addAreaSeries({
topColor: 'red',
bottomColor: 'green',
});
````
Note that every method to create a series has an optional `options` parameter.
1. You can use [`ISeriesApi.applyOptions`](/api/interfaces/ISeriesApi.md#applyoptions) method to apply other options on the fly:
```js
// updating candlestick series options on the fly
candlestickSeries.applyOptions({
upColor: 'red',
downColor: 'blue',
});
```
## Area
- **Method to create**: [`IChartApi.addAreaSeries`](/api/interfaces/IChartApi.md#addareaseries)
- **Data format**: [`SingleValueData`](/api/interfaces/SingleValueData.md) or [`WhitespaceData`](/api/interfaces/WhitespaceData.md)
- **Style options**: a mix of [`SeriesOptionsCommon`](/api/interfaces/SeriesOptionsCommon.md) and [`AreaStyleOptions`](/api/interfaces/AreaStyleOptions.md)
An area chart is basically a colored area between the line connecting all data points and [the time scale](./time-scale.md):
```js chart replaceThemeConstants
const chartOptions = { layout: { textColor: CHART_TEXT_COLOR, background: { type: 'solid', color: CHART_BACKGROUND_COLOR } } };
const chart = createChart(document.getElementById('container'), chartOptions);
const areaSeries = chart.addAreaSeries({ lineColor: LINE_LINE_COLOR, topColor: AREA_TOP_COLOR, bottomColor: AREA_BOTTOM_COLOR });
const data = [{ value: 0, time: 1642425322 }, { value: 8, time: 1642511722 }, { value: 10, time: 1642598122 }, { value: 20, time: 1642684522 }, { value: 3, time: 1642770922 }, { value: 43, time: 1642857322 }, { value: 41, time: 1642943722 }, { value: 43, time: 1643030122 }, { value: 56, time: 1643116522 }, { value: 46, time: 1643202922 }];
areaSeries.setData(data);
chart.timeScale().fitContent();
```
## Bar
- **Method to create**: [`IChartApi.addBarSeries`](/api/interfaces/IChartApi.md#addbarseries)
- **Data format**: [`BarData`](/api/interfaces/BarData.md) or [`WhitespaceData`](/api/interfaces/WhitespaceData.md)
- **Style options**: a mix of [`SeriesOptionsCommon`](/api/interfaces/SeriesOptionsCommon.md) and [`BarStyleOptions`](/api/interfaces/BarStyleOptions.md)
A bar chart shows price movements in the form of bars.
Vertical line length of a bar is limited by the highest and lowest price values.
Open & Close values are represented by tick marks, on the left & right hand side of the bar respectively:
```js chart replaceThemeConstants
const chartOptions = { layout: { textColor: CHART_TEXT_COLOR, background: { type: 'solid', color: CHART_BACKGROUND_COLOR } } };
const chart = createChart(document.getElementById('container'), chartOptions);
const barSeries = chart.addBarSeries({ upColor: BAR_UP_COLOR, downColor: BAR_DOWN_COLOR });
const data = [{ open: 10, high: 10.63, low: 9.49, close: 9.55, time: 1642427876 }, { open: 9.55, high: 10.30, low: 9.42, close: 9.94, time: 1642514276 }, { open: 9.94, high: 10.17, low: 9.92, close: 9.78, time: 1642600676 }, { open: 9.78, high: 10.59, low: 9.18, close: 9.51, time: 1642687076 }, { open: 9.51, high: 10.46, low: 9.10, close: 10.17, time: 1642773476 }, { open: 10.17, high: 10.96, low: 10.16, close: 10.47, time: 1642859876 }, { open: 10.47, high: 11.39, low: 10.40, close: 10.81, time: 1642946276 }, { open: 10.81, high: 11.60, low: 10.30, close: 10.75, time: 1643032676 }, { open: 10.75, high: 11.60, low: 10.49, close: 10.93, time: 1643119076 }, { open: 10.93, high: 11.53, low: 10.76, close: 10.96, time: 1643205476 }];
barSeries.setData(data);
chart.timeScale().fitContent();
```
## Baseline
- **Method to create**: [`IChartApi.addBaselineSeries`](/api/interfaces/IChartApi.md#addbaselineseries)
- **Data format**: [`SingleValueData`](/api/interfaces/SingleValueData.md) or [`WhitespaceData`](/api/interfaces/WhitespaceData.md)
- **Style options**: a mix of [`SeriesOptionsCommon`](/api/interfaces/SeriesOptionsCommon.md) and [`BaselineStyleOptions`](/api/interfaces/BaselineStyleOptions.md)
A baseline is basically two colored areas (top and bottom) between the line connecting all data points and [the base value line](/api/interfaces/BaselineStyleOptions.md#basevalue):
```js chart replaceThemeConstants
const chartOptions = { layout: { textColor: CHART_TEXT_COLOR, background: { type: 'solid', color: CHART_BACKGROUND_COLOR } } };
const chart = createChart(document.getElementById('container'), chartOptions);
const baselineSeries = chart.addBaselineSeries({ baseValue: { type: 'price', price: 25 }, topLineColor: BASELINE_TOP_LINE_COLOR, topFillColor1: BASELINE_TOP_FILL_COLOR1, topFillColor2: BASELINE_TOP_FILL_COLOR2, bottomLineColor: BASELINE_BOTTOM_LINE_COLOR, bottomFillColor1: BASELINE_BOTTOM_FILL_COLOR1, bottomFillColor2: BASELINE_BOTTOM_FILL_COLOR2 });
const data = [{ value: 1, time: 1642425322 }, { value: 8, time: 1642511722 }, { value: 10, time: 1642598122 }, { value: 20, time: 1642684522 }, { value: 3, time: 1642770922 }, { value: 43, time: 1642857322 }, { value: 41, time: 1642943722 }, { value: 43, time: 1643030122 }, { value: 56, time: 1643116522 }, { value: 46, time: 1643202922 }];
baselineSeries.setData(data);
chart.timeScale().fitContent();
```
## Candlestick
- **Method to create**: [`IChartApi.addCandlestickSeries`](/api/interfaces/IChartApi.md#addcandlestickseries)
- **Data format**: [`CandlestickData`](/api/interfaces/CandlestickData.md) or [`WhitespaceData`](/api/interfaces/WhitespaceData.md)
- **Style options**: a mix of [`SeriesOptionsCommon`](/api/interfaces/SeriesOptionsCommon.md) and [`CandlestickStyleOptions`](/api/interfaces/CandlestickStyleOptions.md)
A candlestick chart shows price movements in the form of candlesticks.
On the candlestick chart, open & close values form a solid body of a candle while wicks show high & low values for a candlestick's time interval:
```js chart replaceThemeConstants
const chartOptions = { layout: { textColor: CHART_TEXT_COLOR, background: { type: 'solid', color: CHART_BACKGROUND_COLOR } } };
const chart = createChart(document.getElementById('container'), chartOptions);
const candlestickSeries = chart.addCandlestickSeries({ upColor: BAR_UP_COLOR, downColor: BAR_DOWN_COLOR, borderVisible: false, wickUpColor: BAR_UP_COLOR, wickDownColor: BAR_DOWN_COLOR });
const data = [{ open: 10, high: 10.63, low: 9.49, close: 9.55, time: 1642427876 }, { open: 9.55, high: 10.30, low: 9.42, close: 9.94, time: 1642514276 }, { open: 9.94, high: 10.17, low: 9.92, close: 9.78, time: 1642600676 }, { open: 9.78, high: 10.59, low: 9.18, close: 9.51, time: 1642687076 }, { open: 9.51, high: 10.46, low: 9.10, close: 10.17, time: 1642773476 }, { open: 10.17, high: 10.96, low: 10.16, close: 10.47, time: 1642859876 }, { open: 10.47, high: 11.39, low: 10.40, close: 10.81, time: 1642946276 }, { open: 10.81, high: 11.60, low: 10.30, close: 10.75, time: 1643032676 }, { open: 10.75, high: 11.60, low: 10.49, close: 10.93, time: 1643119076 }, { open: 10.93, high: 11.53, low: 10.76, close: 10.96, time: 1643205476 }];
candlestickSeries.setData(data);
chart.timeScale().fitContent();
```
## Histogram
- **Method to create**: [`IChartApi.addHistogramSeries`](/api/interfaces/IChartApi.md#addhistogramseries)
- **Data format**: [`HistogramData`](/api/interfaces/HistogramData.md) or [`WhitespaceData`](/api/interfaces/WhitespaceData.md)
- **Style options**: a mix of [`SeriesOptionsCommon`](/api/interfaces/SeriesOptionsCommon.md) and [`HistogramStyleOptions`](/api/interfaces/HistogramStyleOptions.md)
A histogram series is a graphical representation of the value distribution.
Histogram creates intervals (columns) and counts how many values fall into each column:
```js chart replaceThemeConstants
const chartOptions = { layout: { textColor: CHART_TEXT_COLOR, background: { type: 'solid', color: CHART_BACKGROUND_COLOR } } };
const chart = createChart(document.getElementById('container'), chartOptions);
const histogramSeries = chart.addHistogramSeries({ color: HISTOGRAM_COLOR });
const data = [{ value: 1, time: 1642425322 }, { value: 8, time: 1642511722 }, { value: 10, time: 1642598122 }, { value: 20, time: 1642684522 }, { value: 3, time: 1642770922, color: 'red' }, { value: 43, time: 1642857322 }, { value: 41, time: 1642943722, color: 'red' }, { value: 43, time: 1643030122 }, { value: 56, time: 1643116522 }, { value: 46, time: 1643202922, color: 'red' }];
histogramSeries.setData(data);
chart.timeScale().fitContent();
```
## Line
- **Method to create**: [`IChartApi.addLineSeries`](/api/interfaces/IChartApi.md#addlineseries)
- **Data format**: [`LineData`](/api/interfaces/LineData.md) or [`WhitespaceData`](/api/interfaces/WhitespaceData.md)
- **Style options**: a mix of [`SeriesOptionsCommon`](/api/interfaces/SeriesOptionsCommon.md) and [`LineStyleOptions`](/api/interfaces/LineStyleOptions.md)
A line chart is a type of chart that displays information as series of the data points connected by straight line segments:
```js chart replaceThemeConstants
const chartOptions = { layout: { textColor: CHART_TEXT_COLOR, background: { type: 'solid', color: CHART_BACKGROUND_COLOR } } };
const chart = createChart(document.getElementById('container'), chartOptions);
const lineSeries = chart.addLineSeries({ color: LINE_LINE_COLOR });
const data = [{ value: 0, time: 1642425322 }, { value: 8, time: 1642511722 }, { value: 10, time: 1642598122 }, { value: 20, time: 1642684522 }, { value: 3, time: 1642770922 }, { value: 43, time: 1642857322 }, { value: 41, time: 1642943722 }, { value: 43, time: 1643030122 }, { value: 56, time: 1643116522 }, { value: 46, time: 1643202922 }];
lineSeries.setData(data);
chart.timeScale().fitContent();
```
## Custom Series (Plugins)
Lightweight Charts offers the ability to add your own custom series types, also known as series plugins. This feature allows developers to extend the functionality of the library by adding new chart types, indicators, or other custom visualizations.
Custom series types can be defined by creating a class which implements the [ICustomSeriesPaneView](/api/interfaces/ICustomSeriesPaneView.md) interface. This class defines the rendering code which Lightweight Charts will use to draw the series on the chart. Once a custom series type is defined, it can be added to any chart instance using the [`addCustomSeries()`](/api/interfaces/IChartApi.md#addcustomseries) method, and be used just like any other series.
Please see the [Plugins](./plugins/intro.md) article for more details.

View File

@ -0,0 +1,37 @@
---
sidebar_position: 3
---
# Time scale
Time scale (or time axis) is a horizontal scale at the bottom of the chart that displays the time of bars.
![Time scale](/img/time-scale.png "Time scale")
Time scale controls a current visible range, allows you to affect or change it, and can convert a time point or [an index](/api/index.md#logical) to a coordinate and vice versa (basically everything related to a x-scale of a chart).
Also, it has a couple of events you can subscribe to to be notified when anything is happened.
To work with time scale you can either change its options or use methods [ITimeScaleApi](/api/interfaces/ITimeScaleApi.md) which could be retrieved by using [`IChartApi.timeScale`](/api/interfaces/IChartApi.md#timescale) method.
All available options are declared in [TimeScaleOptions](/api/interfaces/TimeScaleOptions.md) interface.
Note that you can apply options either via [`ITimeScaleApi.applyOptions`](/api/interfaces/ITimeScaleApi.md#applyoptions) or [`IChartApi.applyOptions`](/api/interfaces/IChartApi.md#applyoptions) with `timeScale` sub-object in passed options - these 2 approaches both have the same effect.
## Logical range
A [logical range](/api/index.md#logicalrange) is an object with 2 properties: `from` and `to`, which are numbers and represent logical indexes on the time scale.
The starting point of the time scale's logical range is the first data item among all series.
Before that point all indexes are negative, starting from that point - positive.
Indexes might have fractional parts, for instance `4.2`, due to the time-scale being continuous rather than discrete.
Integer part of the logical index means index of the fully visible bar.
Thus, if we have `5.2` as the last visible logical index (`to` field), that means that the last visible bar has index 5, but we also have partially visible (for 20%) 6th bar.
Half (e.g. `1.5`, `3.5`, `10.5`) means exactly a middle of the bar.
![Logical range](/img/logical-range.png "Logical range")
Red vertical lines here are borders between bars.
Thus, the visible logical range on the chart above is approximately from `-4.73` to `5.05`.

View File

@ -0,0 +1,114 @@
---
sidebar_position: 4
---
# Working with time zones
This doc describes what do you need to do if you want to add time zone support to your chart.
## Background
By default, `lightweight-charts` doesn't support time zones of any kind, just because JavaScript doesn't have an API to do that.
Things that the library uses internally includes an API to:
- Format a date
- Get a date and/or time parts of a date object (year, month, day, hours, etc)
Out of the box we could rely on 2 APIs:
- [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
- [Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)
And even if to format a date we could (and we do) use `Date` object with its `toLocaleString` method (and we could even pass a `timeZone` field as an option),
but how about date/time field?
All to solve this it seems that the only solution we have is `Date`'s getters, e.g. `getHours`. Here we could use 2 APIs:
- UTC-based methods like `getUTCHours` to get the date/time in UTC
- Client-based methods like `getHours` to get the date/time in _a local (for the client)_ time zone
As you can see we just unable to get date/time parts in desired time zone without using custom libraries (like `date-fns`) out of the box.
Because of this we decided not to handle time zones in the library. The library treats all dates and times as UTC internally.
But don't worry - it's easy to add time-zone support in your own code!
## How to add time zone support to your chart
**TL;DR** - time for every bar should be "corrected" by a time zone offset.
The only way to do this is to change a time in your data.
As soon as the library relies on UTC-based methods, you could change a time of your data item so in UTC it could be as it is in desired time zone.
Let's consider an example.
Lets say you have a bar with time `2021-01-01T10:00:00.000Z` (a string representation is just for better readability).
And you want to display your chart in `Europe/Moscow` time zone.
According to tz database, for `Europe/Moscow` time zone a time offset at this time is `UTC+03:00`, i.e. +3 hours (pay attention that you cannot use the same offset all the time, because of DST and many other things!).
By this means, the time for `Europe/Moscow` is `2021-01-01 13:00:00.000` (so basically you want to display this time over the UTC one).
To display your chart in the `Europe/Moscow` time zone you would need to adjust the time of your data by +3 hours. So `2021-01-01T10:00:00.000Z` would become `2021-01-01T13:00:00.000Z`.
Note that due a time zone offset the date could be changed as well (not only time part).
This looks tricky, but hopefully you need to implement it once and then just forget this ever happened 😀
### `Date` solution
One of possible solutions (and looks like the most simplest one) is to use approach from [this answer on StackOverflow](https://stackoverflow.com/a/54127122/3893439):
```js
// you could use this function to convert all your times to required time zone
function timeToTz(originalTime, timeZone) {
const zonedDate = new Date(new Date(originalTime * 1000).toLocaleString('en-US', { timeZone }));
return zonedDate.getTime() / 1000;
}
```
#### Note about converting to a "local" time zone
If you don't need to work with time zones in general, but only needs to support a client time zone (i.e. local), you could use the following trick:
```js
function timeToLocal(originalTime) {
const d = new Date(originalTime * 1000);
return Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()) / 1000;
}
```
### `date-fns-tz` solution
You could also achieve the result by using [`date-fns-tz`](https://github.com/marnusw/date-fns-tz) library in the following way:
```js
import { utcToZonedTime } from 'date-fns-tz';
function timeToTz(originalTime, timeZone) {
const zonedDate = utcToZonedTime(new Date(originalTime * 1000), timeZone);
return zonedDate.getTime() / 1000;
}
```
### `tzdata` solution
If you have lots of data items and the performance of other solutions doesn't fit your requirements you could try to implement more complex solution by using raw [`tzdata`](https://www.npmjs.com/package/tzdata).
The better performance could be achieved with this approach because:
- you don't need to parse dates every time you want to get an offset so you could use [lowerbound algorithm](https://en.wikipedia.org/wiki/Upper_and_lower_bounds) (which is `O(log N)`) to find an offset of very first data point quickly
- after you found an offset, you go through all data items and check whether an offset should be changed or not to the next one (based on a time of the next time shift)
## Why we didn't implement it in the library
- `Date` solution is quite slow (in our tests it took more than 20 seconds for 100k points)
- Albeit `date-fns-tz` solution is a bit faster that the solution with `Date` but it is still very slow (~17-18 seconds for 100k points) and additionally it requires to add another set of dependencies to the package
- `tzdata` solution requires to increase the size of the library by [more than 31kB min.gz](https://bundlephobia.com/package/tzdata) (which is almost the size of the whole library!)
Keep in mind that time zones feature is not an issue for everybody so this is up to you to decide whether you want/need to support it or not and so far we don't want to sacrifice performance/package size for everybody by this feature.
## Note about converting business days
If you're using a business day for your time (either [object](/api/interfaces/BusinessDay.md) or [string](api/index.md#time) representation), for example because of DWM nature of your data,
most likely you **shouldn't** convert that time to a zoned one, because this time represents a day.

View File

@ -0,0 +1,42 @@
{
"docsSidebar": [
"intro",
"series-types",
"price-scale",
"time-scale",
"time-zones",
{
"Plugins": [
{
"type": "autogenerated",
"dirName": "plugins"
}
]
},
{
"Migrations": [
{
"type": "autogenerated",
"dirName": "migrations"
}
]
},
{
"type": "doc",
"id": "ios",
"label": "iOS"
},
{
"type": "doc",
"id": "android",
"label": "Android"
},
"release-notes"
],
"apiSidebar": [
{
"type": "autogenerated",
"dirName": "api"
}
]
}

View File

@ -1,4 +1,5 @@
[
"4.2",
"4.1",
"4.0",
"3.8"