npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ngx-lightweight-charts

v0.3.6

Published

Angular wrapper for Trading View lightweight-charts

Downloads

125

Readme

ngx-lightweight-charts

An easily extendable Angular wrapper for Trading View lightweight-charts

What it is.

A wrapper that exposes core chart functionality within an Angular app, and allows new functionality to be easily added.

What it's not.

A (re)implementation of the entire lightweight-charts api.

How to use.


  1. Getting started
  2. Displaying a chart
  3. Common chart inputs and outputs
  4. TVChart - accessing the underlying IChartAPI and ISeriesApi instance
  5. Displaying custom data
  6. Implemented behaviour
  7. Adding behaviour

1.

Getting started

| Version | Angular version | |:--------|---------------| | 0.3.x | 18 | | 0.2.x | 17 |

Installation

npm i ngx-lightweight-charts

Add providers

import { ApplicationConfig } from '@angular/core';
import { getTVChartDefaultProviders } from "ngx-lightweight-charts";


export const appConfig: ApplicationConfig = {
  providers: [
    // ...
    getTVChartDefaultProviders()
  ]
};

2.

Displaying a chart

There are two ways:

Either use the tvChart directive, specifying the type of chart to be displayed.

<div tvChart="Line" [data]="lineData"></div>

Or use one of the following convenience components that wrap tvChart to create a specific chart type.

<tv-area-chart [data]="pointData"></tv-area-chart>
<tv-bar-chart [data]="barData"></tv-bar-chart>
<tv-baseline-chart [data]="pointData"></tv-baseline-chart>
<tv-candlestick-chart [data]="klineData"></tv-candlestick-chart>
<tv-histogram-chart [data]="pointData"></tv-histogram-chart>
<tv-line-chart [data]="pointData"></tv-line-chart>

3.

Common chart inputs and outputs

All charts expose the following signal based inputs and outputs:

(Generic type T extends SeriesType and HorzItemScale defaults to Time)

| Input | type | |:---------------------------|:------------------------------------------------| | id | string | | options | DeepPartial<ChartOptions> | | seriesOptions | SeriesPartialOptionsMap[T] | | data | SeriesDataItemTypeMap<HorzScaleItem>[T][] | | markers | SeriesMarker<HorzScaleItem>[] |

| Output | type | |:---------------------------|:--------------------------------------| | initialised | TVChart<T, HorzScaleItem> | | chartClick | MouseEventParams<HorzScaleItem> | | chartDBLClick | MouseEventParams<HorzScaleItem> | | crosshairMoved | MouseEventParams<HorzScaleItem> | | visibleTimeRangeChanged | Range<HorzScaleItem> | | visibleLogicalRangeChanged | LogicalRange | null | | sizeChanged | number | | dataChanged | DataChangedScope |

4.

TVChart - accessing the underlying IChartAPI and ISeriesApi instance

TVChart is the core class that creates, manages and exposes a trading view chart and its associated series.

For convenience TVChart implements the majority of the IChartApi interface and also exposes the IChartApi, ITimeScaleApi and ISeriesApi subscriptions as RXJS streams.

It also exposes the underlying chart, timeScale, priceScale and series through accessors.


Every chart directive/component is simply a container that initialises an injected TVChart instance and exposes a limited set of inputs and outputs to interact with the core functionality of the chart and series.

Once a TVChart has been initialised, there are 2 ways to access it:

1). Through the initialised output of the chart directive/component

import {Component} from "@angular/core";
import {TVChartDirective, TVChart} from "ngx-lightweight-chart";
import {LineData} from "lightweight-charts";

@Component({
  selector: 'my-component',
  standalone: true,
  imports: [
    TVChartDirective
  ],
  template: `
    <div tvChart="Line" [data]="chartData" (initialised)="onChartInit($event)"></div>
  `
})
export class MyComponent {

  chartData: LineData[]

  onChartInit(chart: TVChart<"Line">) {
    //... perform some action through the TVChart API
  }
}

2). Through the tvChartCollector directive when creating reusable functionality, which can be used to access and interact with a single TVChart or a collection.

The tvChartCollector also ensures that all TVChart instances have been fully initialised before exposing them for access through the charts signal.

Accessing a single TVChart instance:

<div tvChart="Line" [data]="chartData" tvChartCollector myDirective></div>
import {Directive, effect, inject} from "@angular/core";
import {TVChartCollectorDirective, TVChart} from "ngx-lightweight-charts";

@Directive({
  selector: '[myDirective]',
  standalone: true
})
export class MyDirective {
  readonly #collector = inject(TVChartCollectorDirective);

  constructor() {
    effect(() => {
      this.#collector.charts()?.forEach((chart: TVChart<any>) => {
        //... perform some action through the TVChart API
      });
    });
  }
}

Accessing multiple TVChart instances:

<div tvChartCollector myMultiChartDirective>
  <tv-candlestick-chart [data]="klineData"></tv-candlestick-chart>
  <tv-histogram-chart [data]="pointData"></tv-histogram-chart>
  <tv-line-chart [data]="pointData"></tv-line-chart>
</div>
import {Directive, effect, inject} from "@angular/core";
import {TVChartCollectorDirective, TVChart} from "ngx-lightweight-charts";

@Directive({
  selector: '[myMultiChartDirective]',
  standalone: true
})
export class MyMultiChartDirective {
  readonly #collector = inject(TVChartCollectorDirective);

  constructor() {
    effect(() => {
      this.#collector.charts()?.forEach((chart: TVChart<any>) => {
        //... perform some action through the TVChart API
      });
    });
  }
}

You may have noticed that the implementation of MyDirective and MyMultiChartDirective are identical. This is intentional. The TVChartCollectorDirective.charts signal always returns an array of charts (whether collecting a single or multiple) allowing the flexibility to easily implement directives or components that work with single and/or multiple charts.

The tvChartCollector also accepts an array of id's to facilitate the filtering of charts by id:

<div [tvChartCollector]="['one, 'two']" myDirective>
  <tv-candlestick-chart id="one" [data]="klineData"></tv-candlestick-chart>
  <tv-histogram-chart id="two" [data]="pointData"></tv-histogram-chart>
  <tv-line-chart [data]="pointData"></tv-line-chart>
</div>
import {Directive, effect, inject} from "@angular/core";
import {TVChartCollectorDirective, TVChart} from "ngx-lightweight-charts";

@Directive({
  selector: '[myDirective]',
  standalone: true
})
export class MyDirective {
  readonly #collector = inject(TVChartCollectorDirective);

  constructor() {
    effect(() => {
      this.#collector.charts()?.forEach((chart: TVChart<any>) => {
        //... perform something only on chart "one" and "two"
      });
    });
  }
}

5.

Displaying custom data

The following example uses the Custom chart HLC area implementation - source code can be found here

Given the following app component:

import {Component} from "@angular/core";

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.component.html'
})
export class AppComponent {
  customSeriesView = new HLCAreaSeries();
  customData = generateAlternativeCandleData(100);
}

There are 2 ways to display custom data:

1). Using the TVChartCustomSeriesComponent

<tv-custom-series-chart [data]="customData" [customSeriesView]="customSeriesView"></tv-custom-series-chart>

Custom series!

2). By adding an additional (custom) series to an existing chart

<div tvChart="Candlestick" [data]="customData" tvChartCollector [customSeriesExample]="customSeriesView"></div>
import {Directive, effect, inject, input} from "@angular/core";
import {TVChartCollectorDirective, TVChart} from "ngx-lightweight-charts";
import {CustomData, CustomSeriesOptions, ICustomSeriesPaneView, ISeriesApi, Time} from "lightweight-charts";


@Directive({
  selector: '[customSeriesExample]',
  standalone: true
})
export class CustomSeriesExampleDirective<HorzScaleItem = Time> {

  readonly #collector = inject(TVChartCollectorDirective);

  data = input<CustomData<HorzScaleItem>[]>();
  customSeriesView = input.required<ICustomSeriesPaneView<HorzScaleItem>>({alias: 'customSeriesExample'});
  seriesOptions = input<CustomSeriesOptions>({} as CustomSeriesOptions);

  #series?: ISeriesApi<'Custom', HorzScaleItem>;

  constructor() {

    effect(() => {
      this.#collector.charts()?.forEach((chart: TVChart<'Candlestick', HorzScaleItem>) => {
        const data = this.data(),
          customSeriesView= this.customSeriesView();

        if(!data || !customSeriesView) {
          return;
        }

        ({
          series: this.#series
        } = chart.addAdditionalSeries('Custom', this.seriesOptions(), customSeriesView));

        this.#series?.setData(data);
      });
    });
  }
}

Additional custom series!

6.

Implemented behaviour


TVChartGroupDirective

Visually groups multiple charts

<div tvChartCollector tvChartGroup>
  <tv-area-chart [data]="pointData"></tv-area-chart>
  <tv-histogram-chart [data]="pointData"></tv-histogram-chart>
  <tv-line-chart [data]="pointData"></tv-line-chart>
</div>

Chart group!


TVChartSyncDirective

Syncs the visible logical range (scale and position) and cross-hair of multiple charts

<div tvChartCollector tvChartSync>
  <tv-candlestick-chart [data]="klineData"></tv-candlestick-chart>
  <tv-histogram-chart [data]="pointData"></tv-histogram-chart>
</div>

Chart sync!


TVChartCrosshairDataDirective

Outputs data relating to the current cross-hair position

Single chart:

<tv-candlestick-chart [data]="klineData" tvChartCollector (tvChartCrosshairData)="onCrosshairData($event)"></tv-candlestick-chart>
import {Component} from "@angular/core";
import {TVChartCrosshairDataDirective} from "ngx-lightweight-charts";

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    TVChartCrosshairDataDirective
  ],
  templateUrl: './app.component.html'
})
export class AppComponent {

  onCrosshairData(data: {[key: string | number]: Record<string, any>}): void {
    /*
      The format of the data is as follows
      {
        [chart id || index]: {
          // cross-hair point data
        }
      }
     */
    
    // do something with data here...
  }
}

Multiple charts:

<div tvChartCollector tvChartSync (tvChartCrosshairData)="onCrosshairData($event)">
  <tv-candlestick-chart id="ohlc" [data]="klines" [options]="{rightPriceScale: {minimumWidth: 80}}"></tv-candlestick-chart>
  <div tvChart="Histogram" [data]="rsiValues" [options]="{rightPriceScale: {minimumWidth: 80}}"></div>
</div>
import {Component} from "@angular/core";
import {OhlcData, HistogramData, Time} from "lightweight-charts";
import {TVChartCrosshairDataDirective} from "ngx-lightweight-charts";

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    TVChartCrosshairDataDirective
  ],
  templateUrl: './app.component.html'
})
export class AppComponent {
  
  klines: OhlcData<Time>[] = [/* loaded kline data */];
  rsiData: HistogramData<Time>[] = [/* loaded rsi data */];

  onCrosshairData(data: {[key: string | number]: Record<string, any>}): void {
    /*
      The format of the data is as follows
      {
        ohlc: {
          time: ...,
          open: ...,
          high: ...,
          low: ...,
          close: ...
        },
        2: {
          time: ...,
          value: ...
        }
      }
     */
    
    // do something with data here...
  }
}

Cross-hair data!

7.

Adding behaviour

To add your own behaviour it's as simple as doing the following:

<div tvChart="Line" [data]="chartData" tvChartCollector yourDirective></div>
import {Directive, effect, inject} from "@angular/core";
import {TVChartCollectorDirective, TVChart} from "ngx-lightweight-charts";

@Directive({
  selector: '[yourDirective]',
  standalone: true
})
export class YourDirective {
  readonly #collector = inject(TVChartCollectorDirective);

  constructor() {
    effect(() => {
      this.#collector.charts()?.forEach((chart: TVChart<any>) => {
        //... perform some action through the TVChart API
      });
    });
  }
}