Lightweight chartsでロウソク足を描画

課題

  • Tradingview が作っている lightweight-charts というライブラリを使ってみたい。
  • インジケータとかは今は不要なので、とりあえずロウソク足だけの、動くグラフを作りたい。
  • 使うデータは websocket で取得して、それをロウソク足に加工してプロットするようにしたい。

使い方の概要

使い方を調べたので、その概要をメモしておく。

  1. chart オブジェクトなるものを、LightweightCharts の createChart メソッドを呼び出して作る。
  2. chart オブジェクトのメソッドを使って、グラフの基になる series オブジェクトなるものを作る。
  3. series オブジェクトの update メソッドなどを使って、series にデータを入れていく。
  4. ブラウザを覗いてみると、グラフが出現している。

実際には1の段階でチャートの枠だけはブラウザに出現していて、series にデータを入れ始めるとグラフが出現するようになっている。

できたチャート

f:id:bitbot:20200610233227p:plain
1秒足

意外と簡単に作れたので驚きだったんだけど、オブジェクトを生成するときのパラメータの設定部分でハマった。ただ各パラメータの意味はドキュメントにちゃんと載っているので、そこはかなり助けられた。ドキュメントってやっぱり大事。*1

コード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Lightweight Charts v3</title>
<script src="https://unpkg.com/lightweight-charts@3.x.x/dist/lightweight-charts.standalone.production.js"></script>
</head>
<body style="margin:0">
<script>
{
// Setup chart
const chart = LightweightCharts.createChart(document.body, {
  width: window.innerWidth,
  height: window.innerHeight,
  layout: {backgroundColor: "#131722", textColor: "#ccc"},
  crosshair: {mode: LightweightCharts.CrosshairMode.Normal},
  timeScale: {rightOffset: 10, timeVisible: true},
  priceScale: {entireTextOnly: true, scaleMargins: {top: 0.1, bottom: 0.22}},
  grid: {
    vertLines: {color: "#363c4e", style: LightweightCharts.LineStyle.Dashed},
    horzLines: {color: "#363c4e", style: LightweightCharts.LineStyle.Dashed},
  },
});

// Create series
const candle = chart.addCandlestickSeries({
  priceFormat: {type: 'custom', formatter: price => Math.floor(price)},
});
const volume = chart.addHistogramSeries({
  priceFormat: {precision: 3},
  priceScaleId: '',
  scaleMargins: {top: 0.8, bottom: 0},
});

// Update series
function updateChart() {
  candle.update({
    time: ohlc[0],
    open: ohlc[1],
    high: ohlc[2],
    low: ohlc[3],
    close: ohlc[4],
  });
  volume.update({
    time: ohlc[0],
    value: ohlc[5],
    color: (ohlc[1] > ohlc[4]) ? "#ef5350" : "#26a69a",
  });
}

// Settings
const SECOND_PERIOD = 1;

// Internal data
let ohlc = [];
let next_time = 0;

// Update internal data
function updateOhlc(msg) {
  const exec_time = Math.floor(new Date(msg.exec_date).getTime() / 1000);
  if (!next_time) {
    ohlc = [exec_time, msg.price, msg.price, msg.price, msg.price, msg.size];
    next_time = exec_time + SECOND_PERIOD;
  } else if (exec_time < next_time) {
    ohlc[2] = (ohlc[2] > msg.price) ? ohlc[2] : msg.price;
    ohlc[3] = (ohlc[3] < msg.price) ? ohlc[3] : msg.price;
    ohlc[4] = msg.price;
    ohlc[5] += msg.size;
  } else if (exec_time < next_time + SECOND_PERIOD) {
    ohlc = [next_time, msg.price, msg.price, msg.price, msg.price, msg.size];
    next_time += SECOND_PERIOD;
  } else {
    ohlc = [next_time, ohlc[4], ohlc[4], ohlc[4], ohlc[4], 0];
    updateChart();
    next_time += SECOND_PERIOD;
    updateOhlc(msg);
  }
  updateChart();
}

// Websocket
const ws = new WebSocket("wss://ws.lightstream.bitflyer.com/json-rpc");
ws.onopen = function(event) {
  ws.send(JSON.stringify({
    method: "subscribe",
    params: {channel: "lightning_executions_FX_BTC_JPY"}
  }));
};
ws.onmessage = function(event) {
  JSON.parse(event.data).params.message.forEach(msg => updateOhlc(msg));
};

// Resize window
window.onresize = () => chart.resize(window.innerWidth, window.innerHeight);
}
</script>
</body>
</html>

※ Websocket が切れた時の再接続はしていないので、接続が切れると描画が止まる。
※ 上記を少し改変したものを GitLab Pages に上げてみた。
https://bitbot125.gitlab.io/lightweight_charts_test/

感想

TradingView 社製だけあって動作が軽くて CPU にも優しい。といっても半日動かしていると(series.updateし続けていると)少しだけ重たくなってくる。都度、古いデータを削除すればいいのかもしれないが分からない。

資料

*1:ドキュメントは、GitHub 上の docs ディレクトリ配下にある。