Skip to content

Commit

Permalink
Merge pull request #1674 from tradingview/curve-charts
Browse files Browse the repository at this point in the history
Add Yield Curve and Options Chart Types, Up/Down Markers Plugin
  • Loading branch information
SlicedSilver authored Dec 9, 2024
2 parents d3cad8e + 56b063b commit 4fd56b6
Show file tree
Hide file tree
Showing 69 changed files with 2,536 additions and 116 deletions.
27 changes: 25 additions & 2 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default [
{
name: 'ESM',
path: 'dist/lightweight-charts.production.mjs',
limit: '45.00 KB',
limit: '47.00 KB',
import: '*',
ignore: ['fancy-canvas'],
brotli: true,
Expand All @@ -30,7 +30,23 @@ export default [
brotli: true,
},
{
name: 'ESM Standalone',
name: 'ESM createYieldCurveChart',
path: 'dist/lightweight-charts.production.mjs',
limit: '45.00 KB',
import: '{ createYieldCurveChart }',
ignore: ['fancy-canvas'],
brotli: true,
},
{
name: 'ESM createOptionsChart',
path: 'dist/lightweight-charts.production.mjs',
limit: '45.00 KB',
import: '{ createOptionsChart }',
ignore: ['fancy-canvas'],
brotli: true,
},
{
name: 'Standalone-ESM',
path: 'dist/lightweight-charts.standalone.production.mjs',
limit: '50.00 KB',
import: '*',
Expand Down Expand Up @@ -114,4 +130,11 @@ export default [
limit: '2.5 KB',
brotli: true,
},
{
name: 'Plugin: UpDownMarkersPrimitive',
path: 'dist/lightweight-charts.production.mjs',
import: '{ createUpDownMarkers }',
ignore: ['fancy-canvas'],
limit: '2.50 KB',
},
];
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ npm install lightweight-charts
```

```js
import { createChart } from 'lightweight-charts';
import { createChart, LineSeries } from 'lightweight-charts';

const chart = createChart(document.body, { width: 400, height: 300 });
const lineSeries = chart.addLineSeries();
const lineSeries = chart.addSeries(LineSeries);
lineSeries.setData([
{ time: '2019-04-11', value: 80.01 },
{ time: '2019-04-12', value: 96.63 },
Expand All @@ -62,7 +62,7 @@ The standalone version creates `window.LightweightCharts` object with all export

```js
const chart = LightweightCharts.createChart(document.body, { width: 400, height: 300 });
const lineSeries = chart.addLineSeries();
const lineSeries = chart.addSeries(LightweightCharts.LineSeries);
lineSeries.setData([
{ time: '2019-04-11', value: 80.01 },
{ time: '2019-04-12', value: 96.63 },
Expand Down
3 changes: 2 additions & 1 deletion debug/debug-esm.mjs.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-env browser */
import {
createChart,
LineSeries,
} from '../dist/lightweight-charts.standalone.development.mjs';

/** @type {HTMLDivElement | null} */
Expand All @@ -17,7 +18,7 @@ const chart = createChart(container, options);
// @ts-ignore expose the chart api for easier debugging
window.chart = chart;

const mainSeries = chart.addLineSeries({
const mainSeries = chart.addSeries(LineSeries, {
priceFormat: {
minMove: 1,
precision: 0,
Expand Down
2 changes: 1 addition & 1 deletion debug/debug-standalone.html.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

const chart = LightweightCharts.createChart(document.getElementById('container'));

const mainSeries = chart.addLineSeries({
const mainSeries = chart.addSeries(LightweightCharts.LineSeries, {
priceFormat: {
minMove: 1,
precision: 0,
Expand Down
3 changes: 2 additions & 1 deletion dts-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"output": {
"sortNodes": true,
"respectPreserveConstEnum": true
}
},
"failOnClass": true
}
]
}
12 changes: 9 additions & 3 deletions src/api/chart-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ function toInternalOptions<HorzScaleItem>(options: DeepPartial<ChartOptionsImpl<
export type IPriceScaleApiProvider<HorzScaleItem> = Pick<IChartApiBase<HorzScaleItem>, 'priceScale'>;

export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, DataUpdatesConsumer<SeriesType, HorzScaleItem> {
protected readonly _horzScaleBehavior: IHorzScaleBehavior<HorzScaleItem>;

private _chartWidget: ChartWidget<HorzScaleItem>;
private _dataLayer: DataLayer<HorzScaleItem>;
private readonly _seriesMap: Map<SeriesApi<SeriesType, HorzScaleItem>, Series<SeriesType>> = new Map();
Expand All @@ -116,7 +118,6 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da

private readonly _timeScaleApi: TimeScaleApi<HorzScaleItem>;
private readonly _panes: WeakMap<Pane, PaneApi<HorzScaleItem>> = new WeakMap();
private readonly _horzScaleBehavior: IHorzScaleBehavior<HorzScaleItem>;

public constructor(container: HTMLElement, horzScaleBehavior: IHorzScaleBehavior<HorzScaleItem>, options?: DeepPartial<ChartOptionsImpl<HorzScaleItem>>) {
this._dataLayer = new DataLayer<HorzScaleItem>(horzScaleBehavior);
Expand Down Expand Up @@ -230,8 +231,8 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
this._sendUpdateToChart(this._dataLayer.setSeriesData(series, data));
}

public updateData<TSeriesType extends SeriesType>(series: Series<TSeriesType>, data: SeriesDataItemTypeMap<HorzScaleItem>[TSeriesType]): void {
this._sendUpdateToChart(this._dataLayer.updateSeriesData(series, data));
public updateData<TSeriesType extends SeriesType>(series: Series<TSeriesType>, data: SeriesDataItemTypeMap<HorzScaleItem>[TSeriesType], historicalUpdate: boolean): void {
this._sendUpdateToChart(this._dataLayer.updateSeriesData(series, data, historicalUpdate));
}

public subscribeClick(handler: MouseEventHandler<HorzScaleItem>): void {
Expand Down Expand Up @@ -336,6 +337,10 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
this._chartWidget.model().clearCurrentPosition(true);
}

public horzBehaviour(): IHorzScaleBehavior<HorzScaleItem> {
return this._horzScaleBehavior;
}

private _addSeriesImpl<
TSeries extends SeriesType,
TData extends WhitespaceData<HorzScaleItem> = SeriesDataItemTypeMap<HorzScaleItem>[TSeries],
Expand Down Expand Up @@ -371,6 +376,7 @@ export class ChartApi<HorzScaleItem> implements IChartApiBase<HorzScaleItem>, Da
model.updateTimeScale(update.timeScale.baseIndex, update.timeScale.points, update.timeScale.firstChangedPointIndex);
update.series.forEach((value: SeriesChanges, series: Series<SeriesType>) => series.setData(value.data, value.info));

model.timeScale().recalculateIndicesWithData();
model.recalculateAllPanes();
}

Expand Down
19 changes: 10 additions & 9 deletions src/api/create-chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ import { IHorzScaleBehavior } from '../model/ihorz-scale-behavior';
import { ChartApi } from './chart-api';
import { IChartApiBase } from './ichart-api';

export function fetchHtmlElement(container: string | HTMLElement): HTMLElement {
if (isString(container)) {
const element = document.getElementById(container);
assert(element !== null, `Cannot find element in DOM with id=${container}`);
return element;
}
return container;
}

/**
* This function is the main entry point of the Lightweight Charting Library. If you are using time values
* for the horizontal scale then it is recommended that you rather use the {@link createChart} function.
Expand All @@ -26,15 +35,7 @@ export function createChartEx<HorzScaleItem, THorzScaleBehavior extends IHorzSca
horzScaleBehavior: THorzScaleBehavior,
options?: DeepPartial<ReturnType<THorzScaleBehavior['options']>>
): IChartApiBase<HorzScaleItem> {
let htmlElement: HTMLElement;
if (isString(container)) {
const element = document.getElementById(container);
assert(element !== null, `Cannot find element in DOM with id=${container}`);
htmlElement = element;
} else {
htmlElement = container;
}

const htmlElement = fetchHtmlElement(container);
const res = new ChartApi<HorzScaleItem>(htmlElement, horzScaleBehavior, options);
horzScaleBehavior.setOptions(res.options());
return res;
Expand Down
25 changes: 25 additions & 0 deletions src/api/create-options-chart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { DeepPartial } from '../helpers/strict-type-checks';

import { HorzScaleBehaviorPrice } from '../model/horz-scale-behavior-price/horz-scale-behaviour-price';
import { PriceChartOptions } from '../model/horz-scale-behavior-price/options';

import { createChartEx } from './create-chart';
import { IChartApiBase } from './ichart-api';

/**
* Creates an 'options' chart with price values on the horizontal scale.
*
* This function is used to create a specialized chart type where the horizontal scale
* represents price values instead of time. It's particularly useful for visualizing
* option chains, price distributions, or any data where price is the primary x-axis metric.
*
* @param container - The DOM element or its id where the chart will be rendered.
* @param options - Optional configuration options for the price chart.
* @returns An instance of IChartApiBase configured for price-based horizontal scaling.
*/
export function createOptionsChart(
container: string | HTMLElement,
options?: DeepPartial<PriceChartOptions>
): IChartApiBase<number> {
return createChartEx(container, new HorzScaleBehaviorPrice(), options);
}
31 changes: 31 additions & 0 deletions src/api/create-yield-curve-chart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { DeepPartial } from '../helpers/strict-type-checks';

import {
YieldCurveChartOptions,
} from '../model/yield-curve-horz-scale-behavior/yield-curve-chart-options';

import { fetchHtmlElement } from './create-chart';
import { IYieldCurveChartApi } from './iyield-chart-api';
import { YieldChartApi } from './yield-chart-api';

/**
* Creates a yield curve chart with the specified options.
*
* A yield curve chart differs from the default chart type
* in the following ways:
* - Horizontal scale is linearly spaced, and defined in monthly
* time duration units
* - Whitespace is ignored for the crosshair and grid lines
*
* @param container - ID of HTML element or element itself
* @param options - The yield chart options.
* @returns An interface to the created chart
*/
export function createYieldCurveChart(
container: string | HTMLElement,
options?: DeepPartial<YieldCurveChartOptions>
): IYieldCurveChartApi {
const htmlElement = fetchHtmlElement(container);
const chartApi = new YieldChartApi(htmlElement, options);
return chartApi;
}
9 changes: 6 additions & 3 deletions src/api/ichart-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ChartOptionsImpl } from '../model/chart-model';
import { BarData, HistogramData, LineData, WhitespaceData } from '../model/data-consumer';
import { Time } from '../model/horz-scale-behavior-time/types';
import { CustomData, ICustomSeriesPaneView } from '../model/icustom-series';
import { IHorzScaleBehavior } from '../model/ihorz-scale-behavior';
import { Point } from '../model/point';
import {
CustomSeriesOptions,
Expand Down Expand Up @@ -125,9 +126,6 @@ export interface IChartApiBase<HorzScaleItem = Time> {
/**
* Creates a series with specified parameters.
*
* A custom series is a generic series which can be extended with a custom renderer to
* implement chart types which the library doesn't support by default.
*
* @param definition - A series definition.
* @param customOptions - Customization parameters of the series being created.
* @param paneIndex - An index of the pane where the series should be created.
Expand Down Expand Up @@ -341,4 +339,9 @@ export interface IChartApiBase<HorzScaleItem = Time> {
* @returns Dimensions of the chart pane
*/
paneSize(paneIndex?: number): PaneSize;

/**
* Returns the horizontal scale behaviour.
*/
horzBehaviour(): IHorzScaleBehavior<HorzScaleItem>;
}
6 changes: 4 additions & 2 deletions src/api/iseries-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ export interface ISeriesApi<
*
* @param bar - A single data item to be added. Time of the new item must be greater or equal to the latest existing time point.
* If the new item's time is equal to the last existing item's time, then the existing item is replaced with the new one.
* @param historicalUpdate - If true, allows updating an existing data point that is not the latest bar. Default is false.
* Updating older data using `historicalUpdate` will be slower than updating the most recent data point.
* @example Updating line series data
* ```js
* lineSeries.update({
Expand All @@ -175,7 +177,7 @@ export interface ISeriesApi<
* });
* ```
*/
update(bar: TData): void;
update(bar: TData, historicalUpdate?: boolean): void;

/**
* Returns a bar data by provided logical index.
Expand Down Expand Up @@ -270,7 +272,7 @@ export interface ISeriesApi<
* @returns Type of the series.
* @example
* ```js
* const lineSeries = chart.addLineSeries();
* const lineSeries = chart.addSeries(LineSeries);
* console.log(lineSeries.seriesType()); // "Line"
*
* const candlestickSeries = chart.addCandlestickSeries();
Expand Down
5 changes: 5 additions & 0 deletions src/api/iseries-primitive-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Time } from '../model/horz-scale-behavior-time/types';
import { IHorzScaleBehavior } from '../model/ihorz-scale-behavior';
import { ISeriesPrimitiveBase } from '../model/iseries-primitive';
import { SeriesOptionsMap, SeriesType } from '../model/series-options';

Expand All @@ -25,6 +26,10 @@ export interface SeriesAttachedParameter<
* Request an update (redraw the chart)
*/
requestUpdate: () => void;
/**
* Horizontal Scale Behaviour for the chart.
*/
horzScaleBehavior: IHorzScaleBehavior<HorzScaleItem>;
}

/**
Expand Down
31 changes: 31 additions & 0 deletions src/api/iyield-chart-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { LineData, WhitespaceData } from '../model/data-consumer';
import { SeriesPartialOptionsMap } from '../model/series-options';
import { SeriesDefinition } from '../model/series/series-def';

import { IChartApiBase } from './ichart-api';
import { ISeriesApi } from './iseries-api';

export type YieldCurveSeriesType = 'Area' | 'Line';

/**
* The main interface of a single yield curve chart.
*/
export interface IYieldCurveChartApi extends Omit<IChartApiBase<number>, 'addSeries'> {
/**
* Creates a series with specified parameters.
*
* Note that the Yield Curve chart only supports the Area and Line series types.
*
* @param definition - A series definition for either AreaSeries or LineSeries.
* @param customOptions - Customization parameters of the series being created.
* @param paneIndex - An index of the pane where the series should be created.
* ```js
* const series = chart.addSeries(LineSeries, { lineWidth: 2 });
* ```
*/
addSeries<T extends YieldCurveSeriesType>(
definition: SeriesDefinition<T>,
options?: SeriesPartialOptionsMap[T],
paneIndex?: number
): ISeriesApi<T, number, WhitespaceData<number> | LineData<number>>;
}
1 change: 1 addition & 0 deletions src/api/options/time-scale-options-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export const timeScaleOptionsDefaults: HorzScaleOptions = {
uniformDistribution: false,
minimumHeight: 0,
allowBoldLabels: true,
ignoreWhitespaceIndices: false,
};
7 changes: 7 additions & 0 deletions src/api/options/yield-curve-chart-options-defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { YieldCurveOptions } from '../../model/yield-curve-horz-scale-behavior/yield-curve-chart-options';

export const yieldChartOptionsDefaults: YieldCurveOptions = {
baseResolution: 1,
minimumTimeRange: 120,
startTimeRange: 0,
};
5 changes: 3 additions & 2 deletions src/api/series-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ export class SeriesApi<
this._onDataChanged('full');
}

public update(bar: TData): void {
public update(bar: TData, historicalUpdate: boolean = false): void {
checkSeriesValuesType(this._series.seriesType(), [bar]);

this._dataUpdatesConsumer.updateData(this._series, bar);
this._dataUpdatesConsumer.updateData(this._series, bar, historicalUpdate);
this._onDataChanged('update');
}

Expand Down Expand Up @@ -227,6 +227,7 @@ export class SeriesApi<
chart: this._chartApi,
series: this,
requestUpdate: () => this._series.model().fullUpdate(),
horzScaleBehavior: this._horzScaleBehavior,
});
}
}
Expand Down
Loading

0 comments on commit 4fd56b6

Please sign in to comment.