vue-chart.jsに関しては私の記事で色々実験しているのでvue-chart関連の記事を参考に別のページも回って欲しい。
この記事は次に書く予定の「板差分からスプレッドを計算する」につなげる記事であり、この記事だけでは実用性にかける。
Best ASK - Best Bid = Spread
この計算式だけだ。一応混乱がないように説明すると
この買い板と売り板の差がスプレッドと呼ばれる。
mmbotはこの幅を細かく売り買いすることで収益を上げる仕組みであるためmmbot制作にスプレッドの理解は必須事項だ(私のBLOGはmm制作がメイン目標ではないので注意)
今回も引き続きbitFlyerのRealtime APIからの板情報を元にスプレッドを計算していく。
スプレッドに関してはTickerを使っても計算できるが最も情報が早いのが「板の差分」情報であるため、こちらからスプレッドを計算していくのがベストだ。
また通信にはSocket.ioを利用している。bitFlyerのSocket.ioによるAPI取得はかなり詳しく解説したので自動売買システムのタグ一覧の最初から記事を読んでもらえば理解できるはずだ。
冒頭でも書いたが本日はスナップショットを利用して練習バージョンを作成する。いきなり高速な差分情報を元に計算していくとエラーの温床となるため前段階の記事とした。
※今回はSnapshotのみの実装なので非常に更新が遅い。動画は3倍速になっている。
まずはsnapshotを受信する。
socket.on("lightning_board_snapshot_FX_BTC_JPY", message => {
this.snapshotMessage = message //テスト用
});
messageをsnapshotMessage
の変数に格納して保存。bitFlyerから受け取ったデータをそのまま送っているだけだ。
this.snapshotMessage
はdata部で {}
にて空のオブジェクトで定義されている。
メインのvueからSpread
という名前の子コンポーネントとして登録した。
この時にsnapshotMessage
を引き渡すこと。内部でも同じ変数名を利用する。
<!-- スプレッドコンポーネント -->
<Spread :spreadBoardData="snapshotMessage" />
子コンポーネントの中身
<template>
<div>
<SpreadChart :chart-data="datacollection" :options="options" />
</div>
</template>
<script>
import SpreadChart, { renewSpread } from "./SpreadChart.js";
export default {
components: {
SpreadChart
},
props: ["spreadBoardData"],
data: () => ({
datacollection: {
labels: [],
datasets: [
{
label: [],
data: []
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
yAxes: [
{
position: "right"
}
]
}
},
maxCnt: 30
}),
methods: {
},
watch: {
spreadBoardData() {
this.datacollection = renewSpread(
this.spreadBoardData,
this.datacollection,
this.maxCnt
);
}
}
};
</script>
ここで注意すべきはdata
部にてdatacollection
には必ずキー名を列挙しておくということ。これを怠ってnull
等で定義するとVue.jsの仕様によりリアクティブではないデータになってしまう(データを入れてもチャートが更新されない)
またここで一緒にoptions
の初期値を設定しておく。
maintainAspectRatio
: trueにしてしまうとディスプレイの高さ一杯に描画されるので見にくくなる。基本false
で良い。position
: Y軸のラベルを右側に表示するようにしている。親から受け取ったsnapshotMessage
を監視しこれが更新されたときに内部のコードが逐一実行される仕組み。
ここでは次で説明するrenewSpread
関数にメッセージを引き渡している。
以前説明したがvue-chart.jsの仕様でグラフを描画するコードは必ずファイルを分けて読み込む。
ファイルが2つ必要な仕組みについては以前の記事:やたら易しいvue-chart.js解説 にて解説しているので参照してほしい。
合わせてこちらののファイルに先程のrenewSpread
関数を定義した。
renewSpread
関数import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default {
mixins: [Line, reactiveProp],
props: ['options'],
mounted() {
this.renderChart(this.data, this.options)
}
}
export const renewSpread = (message, datacollection, maxCnt) => {
let best_bid_price = message.bids[0].price;
let best_ask_price = message.asks[0].price;
//bid max
for (const d of message.bids) {
best_bid_price = d.price > best_bid_price ? d.price : best_bid_price;
}
//asks min
for (const d of message.asks) {
best_ask_price = d.price < best_ask_price ? d.price : best_ask_price;
}
if (best_ask_price < best_bid_price) {
console.log("err: best bid > best ask");
}
const spread = best_ask_price - best_bid_price;
//ラベルは「時:分:秒」で表示
const date = new Date();
const time =
date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
datacollection.labels.push(time);
datacollection.datasets[0].data.push(spread)
//画面に表示する件数を超えたらshiftで削る
while (datacollection.labels.length > maxCnt) {
datacollection.labels.shift()
datacollection.datasets.forEach((array) => {
array.data.shift()
})
}
const return_datacollection = {
labels: datacollection.labels,
datasets: [
{
label: "Spread",
data: datacollection.datasets[0].data,
fill: false,
borderColor: "blue"
},
]
}
return return_datacollection
}
bitFlyerからのデータが直接入っているだけのmessage
からBest Ask, Best Bidを抽出しspread
という定数に格納している。
ここで最も重要なのはアルゴリズムではなくdatacollection
を引数で受け取っているにもかかわらずわざわざconst return_datacollection
として全く同じ構造の新しいオブジェクトをreturn
している点だが、これはvue-chart.jsの仕様によるものである。
datacollection = 参照渡しではなく新しいdatacollection
というコードにしないと描画がリアクティブにならないので覚えておきたい。
またmaxCnt
によりしきい値を設け一定の件数を上回り次第左側のグラフデータを1件削って必要以上に表示しないようにしている。
このコードを書いていて気づいたことを書いておく。
options
はリアクティブではないがdatacollection
が描画される時に変更できる
options
は一度設定したら変更出来ないものだと思っていた。変更できないのではなくリアクティブでないだけなので注意だ。
data部のdatacollection
の値には何も入れてはいけない
データセット名が「Spread」のみなのでdatasets[0].label
の値に初め初期値として"Spread"
を入れていたが、コード上から上書きしようとするとエラーが出た。詳しくは不明。
今回は内容が簡単であると同時に今までvue-chart.jsを何度も使ってきたので比較的短時間に実装できた。
じっくり見ていると数百円のスプレッドは頻繁にあらわれるためmmbotというものの存在も頷ける。
次は「差分」を使って高速に更新していこうと考えているが、若干コードが複雑になりそうだ。
※この記事の内容は公開されている謎のチャートに実装していない。 ある程度まとまってから公開版への実装を予定している。