Angular에서 Trading View Chart Library 다루기 updated_at: 2025-06-13 16:35

Trading View Chart Library

Github Chart library

[github] (https://github.com/tradingview/charting_library) 사용설명

차트 라이브러리의 소스는 위의 github에서 가져올 수 있지만 접근 제한이 걸려 있다.
이것을 사용하기 위해서는 github 접근 허가를 받아야 한다.

트레이딩 뷰 차트 라이브러리 신청하기

신청폼 url

혹은 https://www.tradingview.com/free-charting-libraries/ 에서 "Advanced Charts" 에서 "Learn more" 클릭후 "Get the libray" 클릭

Angular 에서 시작하기

import {
  widget,
  IChartingLibraryWidget,
  ChartingLibraryWidgetOptions,
  LanguageCode,
  ThemeName,
  Timezone,
  IBasicDataFeed,
  ResolutionString,
  ResolutionBackValues,
  HistoryDepth
} from './charting_library.min';
..........
export class TvChartContainerComponent {
  constructor() {
    const widgetOptions = {} // 아래 별도의 widgetOption 설명
    this._tvWidget = new widget(widgetOptions);
    this._tvWidget.onChartReady(() => {})
  }
  
}

widgetOptions

symbol

예 : Coinbase:BTC/USD

datafeed

예 : new (window as any).Datafeeds.UDFCompatibleDatafeed(this._datafeedUrl),

interval

resolution 이나 interval은 바의 시간이다.
default resolution 을 세팅 할 수 있다.

new TradingView.widget({
  interval: '1D',
});

container_id: this._containerId,

library_path: this._libraryPath,

locale

예 : 'ko',

load_last_chart: false,

disabled_features: [

// 'left_toolbar', 'edit_buttons_in_legend', 'hide_last_na_study_output', 'symbol_info', 'dont_show_boolean_study_arguments', 'legend_context_menu', 'display_market_status', 'use_localstorage_for_settings', // local에 저장되어있는 세팅 // 'header_widget', // 상단 툴바 'timeframes_toolbar', // 하단 툴바 'widget_logo' ],

charts_storage_url: this._chartsStorageUrl, // 추가

client_id: this._clientId,

user_id: this._userId,

fullscreen: this._fullscreen,

autosize: this._autosize,

hide_top_toolbar: false,

style: 1,

timezone: this.timezone,

theme (ThemeName):

'Dark';

overrides:

{
  'mainSeriesProperties.showCountdown': false, // 추가
  'paneProperties.background': this.themeColorObj.bg, //백그라운드 배경색
  'paneProperties.vertGridProperties.color': this.themeColorObj.vertiLine,
  'paneProperties.horzGridProperties.color': this.themeColorObj.horiLine,
  'symbolWatermarkProperties.transparency': 90,
  'paneProperties.topMargin': 15,
  'paneProperties.bottomMargin': 15,
  'scalesProperties.textColor': this.themeColorObj.txt, // 전체 글자컬러 색
  'mainSeriesProperties.candleStyle.upColor': '#d3225d',
  'mainSeriesProperties.candleStyle.downColor': '#4094e8',
  'mainSeriesProperties.candleStyle.drawWick': true,
  'mainSeriesProperties.candleStyle.drawBorder': true,
  'mainSeriesProperties.candleStyle.borderUpColor': '#d3225d',
  'mainSeriesProperties.candleStyle.borderDownColor': '#4094e8',
  'mainSeriesProperties.candleStyle.wickUpColor': '#d3225d',
  'mainSeriesProperties.candleStyle.wickDownColor': '#4094e8',
  'mainSeriesProperties.candleStyle.barColorsOnPrevClose': false,
}

Method

onChartReady

tv chart가 준비된 상태인지 확인

this._tvWidget.onChartReady(() => {})

activeChart

활성화된 차트에 대해서 다양한 프로그램을 처리한다.

setVisibleRange

차트에서 볼 수 있는 시간 구간을 프로그래밍적으로 설정

tvWidget.activeChart().setVisibleRange(range: {from: number, to: number})
  • from: Unix timestamp (초 단위) - 범위 시작 시간
  • to: Unix timestamp (초 단위) - 범위 종료 시간
this._tvWidget.activeChart().setVisibleRange({from,to}, {
  percentRightMargin: 1,
  applyDefaultRightMargin: false
}).then(() => {});
setVisibleBars

시간뿐 아니라 바 인덱스 기반으로 설정 가능

getVisibleRange

현재 보이는 범위 확인

chart

setChartType
this._tvWidget.chart().setChartType(1);  // candles
this._tvWidget.chart().setChartType(2); // lines
createMultipointShape

다음과 같은 복수 지점을 기반으로 하는 도형(Shape)을 생성할 때 사용

  • 추세선 (Trend Line)
  • 평행 채널 (Parallel Channel)
  • 삼각형 (Triangle)
  • 피보나치 도구 등
createMultipointShape(points: Array<ChartPoint>, options: MultipointShapeOptions)
const shape = chart.createMultipointShape(
  [
    { time: 1718250000, price: 42000 }, // 첫 번째 지점
    { time: 1718253600, price: 43000 }  // 두 번째 지점
  ],
  {
    shape: 'trend_line', // 도형 종류
    disableSelection: false,
    disableSave: false,
    disableUndo: false,
    zOrder: 'top',
    overrides: {
      linecolor: '#ff0000',
      linewidth: 2
    }
  }
);
this._tvWidget.chart().createMultipointShape([{
      time: this.gameResult.time,
      price: this.gameResult.price,
  }], {
      shape: 'arrow_up',
      lock: true,
      text: this.gameResult.min + '',
      disableSelection: true,
      disableSave: true,
      disableUndo: true,
      overrides: [{
          backgroundColor: '#F55F72',
          color: '#F55F72',
          linecolor: '#F55F72',
          fontsize: 12,
      }]
  });

지원하는 shape 종류는 아래등이 있으나 tv의 라이브러리 버전에 따라 다르므로 공식문서등을 참조 바랍니다.

  • trend_line: 일반 추세선
  • parallel_channelㅍ평행 채널
  • fib_retracement: 피보나치 되돌림 도구
  • triangle: 삼각형
  • path: 자유 형식 경로 (custom path)
createOrderLine

주문 라인을 그려줄때 사용한다. (현재 나의 주문 금액이랑 주문량등등)

this.orderLine = this._tvWidget.chart().createOrderLine({
  price: 1500, // 표시할 가격
  lineColor: '#0ac7cb', // 선 색상
  lineWidth: 2, // 선 두께
  lineStyle: 0, // 0: 실선, 1: 점선, 2: 파선 등
  lineLength: 3, // 선 길이 (기본: 2~3)
  text: 'Take Profit', // 라벨 텍스트 (선택사항)
  quantity: 1, // 주문 수량 (선택사항)
  quantityFont: { // 수량 표시 폰트 (선택사항)
    size: 12,
    family: 'Arial',
    color: '#000'
  }
});

특정 price에서 라인만 그려줄 경우는 위와 같이 하면 이상하게 나온다.
특히 가격라인이 정확하게 그려지지 않는 경우도 있다.
버전에 따른 차이인지는 모르겠지만 본인은 아래와 같이 처리하였다.

this.orderLine = this._tvWidget.chart().createOrderLine({})
  .setText('')
  .setQuantity('')
  .setLineColor('#db713b')
  .setPrice(1500);

삭제할 경우는 아래와 같이 처리한다.

// await this.orderLine.setPrice(null);
await this.orderLine.remove();
this.orderLine = null;  

data 처리하기

data는 크게 두가지 방식일 것이다. 초기데이타 및 실시간 업데이트 데이타 일 것이다.
여기서는 api를 이용하여 json 파일을 받아서 초기 세팅용으로 처리하는 데이타와 이후 nodejs io를 이용하여 실시간으로 받는 데이타를 예로 설명드립니다.

  • history.service.ts

초기 데이타를 api를 통해 가져옮

import { HttpClient } from '@angular/common/http'; 

export class HistoryService {
  constructor(private http: HttpClient) { }

  private handleError(error: Response): any {}

  public async getHistoryData(): Promise<any> {
    const baseUrl = [Your API URL];
    return new Promise((resolve) => {
      this.http.get(this.baseUrl).subscribe({
        next: (data) => {resolve(data)},
        error: (err) => this.handleError(err)
      });
    });
  }
}

  • socket.service.ts

socket에 접속하여 실시간 데이타 수신

import { Injectable, OnDestroy } from '@angular/core';
import { SocketMultiService } from 'ng-node-socket';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';

..........
export class SocketService {
  private socket: any;
  private _subscriptions = [];
  private ngUnsubscribe = new Subject();

  constructor(
    private sockets: SocketMultiService,
  ) {}

  public subscribeBars(updateCb: any): void {
    this.sockets.On('fx', 'game_tick')
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe({
        next: (data: any) => {
          const lastBarData = {
            time: data[0].date * 1000,
            low: data[0].low,
            high: data[0].high,
            open: data[0].open,
            close: data[0].close
          };

          updateCb(lastBarData);
        },
      });
  }

  public unsubscribeBars(uid: string): void {
    const subIndex = this._subscriptions.findIndex((e: any) => e.uid === uid);
    if (subIndex === -1) {
      return;
    }
    const sub: any = this._subscriptions[subIndex];
    this.socket.emit('SubRemove', {
      subs: [sub.channelString]
    });
    this._subscriptions.splice(subIndex, 1);
  }
}
  • tv-chart.ts
import { HistoryService } from './history.service';
import { SocketService } from './socket.service';
export class TvChartContainerComponent {
  constructor(
    private tradeHistory: TradeHistoryService,
    private socketService: SocketService,
  ) {
  }


  private loadTradingViewData(): void {

    const widgetOptions: any = {
      ..........
      datafeed: this.Datafeed,
    }

    this._tvWidget = new widget(widgetOptions);

    this.Datafeed = {
      ..........
      getBars: (symbolInfo, resolution, from, to, onHistoryCallback, onErrorCallback, firstDataRequest) => {
        // sending 2000 default limit
        this.tradeHistory.getHistoryData().then((res) => {
          if (res.error) {
            onHistoryCallback([], {
              noData: true
            });
          }

          if (res.data.length) {
            const bars = res.data.map((el: any) => {
              return {
                time: el.date * 1000, // 미리 세컨드로 변경
                low: el.low,
                high: el.high,
                open: el.open,
                close: el.close,
                volume: 0
              };
            });

            if (firstDataRequest) {
              const lastBar = bars[bars.length - 1];
              history[symbolInfo.name] = {
                lastBar
              };
            }

            if (bars.length) {
              onHistoryCallback(bars, {
                noData: false
              });
            } else {
              onHistoryCallback([], {
                noData: true
              });
            }
          } else {
            onHistoryCallback([], {
              noData: true
            });
          }
        });
      },

      subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {
        this.socketService.subscribeBars(onRealtimeCallback);
      },
      unsubscribeBars: (subscriberUID) => {
        this.socketService.unsubscribeBars(subscriberUID);
      },
    ..........
    };
  }
}
 

평점을 남겨주세요
평점 : 5.0
총 투표수 : 1

질문 및 답글