bitbankのCandlestick APIを使ってAngularでチャートを描画する
多くの仮想通貨取引所では、APIトレーダー向けに取引のための注文APIや価格情報を取得するためのAPIが公開されている。
今回は、bitbankのpublic APIのうちcandlestickを使ってAngularと ngx-chartsを使ってチャートを描画してみたので、ポイントとサンプルコードを記録しておく。
Candlestickについて
今回使用するcandlestick APIは、ろうそく足チャートを描画するのに必要な情報を取得できるAPIで、以下の情報が含まれる。
- 期間開始時の価格
- 期間内の最高値
- 期間内の最安値
- 期間終了時の価格
- 期間内の出来高合計
OHLCVデータ
CandlestickデータはOHLCVというフォーマット(フォーマットというより並び順)で配信されていて、それぞれは以下の情報とマッピングすることができる。
実際にはOHLCV + 期間を示すタイムスタンプが配信されている。
ちなみに、OHLVまでを 四本値 と呼んだりもするらしい。
- open(O) :期間開始時の価格
- high(H) :期間内の最高値
- low(L) :期間内の最安値
- close(C) :期間終了時の価格
- volume(V) :期間内の出来高合計
- ※timestamp :UnixTime
参考: https://docs.bitbank.cc/#!/Candlestick/candlestick
OHLCVとngx-chartsのseriesとのマッピング
今回作ったサンプルでは、ロウソクやヒゲは描画せず、単純なlineチャートとして表現したいので、OHLCVから必要な価格情報とtimestampのみを使用して、ngx-chartsのseriesの形にマッピングする。
といっても、以下のようなTypeScriptのインターフェースを定義して、RxJSのmapオペレータの中で単純なマップをするだけ。
// ngx-chartsに渡すデータのtype (nameがx,valueがy軸に対応する) export interface Series { name: number; value: number; }
次のようなマッピング用のメソッドを用意して、cnaclestickの6番目の要素とのtimestampと1番目の要素の開始時の価格でlineチャートを描画する情報を作っていく。
private mapSeries(candleStick: CandleStick): Series[] { return candleStick.ohlcv.map((data) => ({ name: data[5], value: +data[0], })); }
実際にはRxJsのmapオペレータの中で上記のマッピング用のメソッドを呼び出す。
private fetchCandlestick( pair: string, unit: string, period: string ): Observable<Series[]> { const url = this.getCnadlestickUrl(pair, unit, period); return this.http.get<BbApiResponse<BbCandleStickResponse>>(url).pipe( map((data) => data.data.candlestick[0]), map((candlestick) => this.mapSeries(candlestick)) ); }
完成したもの
リポジトリはこれ
daikiojm/angular-bitbank-public-chart -GitHub
StackBlitzで動いてるやつ
daikiojm/angular-bitbank-public-chart -StackBlitz
通過ペアと期間を指定して、現在から1日前、1週間前、1ヶ月前、1年前それぞれの価格lineチャートが取得できるようになっている。
かなりざっくりな実装だが、価格推移の雰囲気を表現するのには十分じゃないかと思う。
各取引所でこういったpublicデータを配信するAPIがが公開されているので、リアルタイムデータと合わせてまとめサイトみたいなのを作ってみても面白そう。
参考
-
- bitbank公式のAPIドキュメント
-
- 公式のサンプル一覧
【NGX-Charts】超絶素晴らしいAngular6対応のグラフライブラリがあるからみんなに知ってほしい -Developers.IO
- クラスメソさんAWSの会社のイメージあるけどAngular関連の記事もたまに出してる印象ある
ErgoDox EZのファームウェアをMacでビルドしようとしたら辛かったのでDcokerでビルドした
ErgoDox EZは、GUIからキーマップを作成して、.hex
ファイルのファームウェアをダウンロードできるサイトを用意してくれている。
ただ、ここから変更できる内容には制限があり、少し凝ったキーマップに変更しようとすると、キーマップファイルを自分で修正してソースからビルドする必要がある。 キーマップの作成時には、以下の記事が大変参考になった。
自分の場合、Macの Command
キーを複数のキーに割り当てたくなりGUIからのキーマップ作成を諦め、ソースからビルドすることを決心した。
Macでのローカルビルドを試みた
MacでErgoDox EZのファームウェアをローカルビルドするには avr-gcc
というavrマイコン用のビルドツールをインストールすればいいことがわかった。
brewで一発インストールできるとの記事を見て、軽い気持ちで試みるもmake実行中にコンパイルエラーが発生し、brewやビルドツール周りの環境を確認するも未だに解決していない状態。
一刻も早くキーマップを変更して快適なErgoDoxライフを送りたかったので、homebrew-avr のリポジトリにIssueを投げつつ、Dockerで用意されたビルド環境を使うことにした。
今回の状況は、以下のIssueの通り
Dockerでビルド
ErgoDox EZのファームウェアが配布されているリポジトリ qmk/qmk_firmware にビルド用のDockerfileがあったので、これを使い使ってみる。 ひとまず、リポジトリに含まれているデフォルトのキーマップをビルドする。
クローンしておく
$ git clone git@github.com:qmk/qmk_firmware.git $ cd qmk_firmware
Dockerfileからイメージをビルド
あとで気づいたが、 edasque/qmk_firmware を使ってもいいかもしれない。
$ docker build -t qmk_firmware .
対象のキーボードの種類と作成した(今回はデフォルト)キーマップを環境変数に指定して、dockerを起動する
$ docker run -e keymap=default -e keyboard=ergodox_ez --rm -v (pwd):/qmk:rw qmk_firmware
正常にビルドできれば、 ergodox_ez_default.hex
というファイルが出力されているはず。
終わりに
ひとまず、当初考えていたキーマップに変更することができて概ね満足です。
まだまだ、ErgoDox自体に慣れるにまでに時間がかかりそうで、暇な時にタイピングゲームとかしている状況。
(学生の頃にイキってUS配列のMacを使い始めたものの、なかなか慣れなかったときの感覚)
このブログもErgoDoxで書いてるけど、正直苦行なので早く慣れていきたい。
ScottyでAngularSPAをS3+CloudFrontにデプロイ
CI環境上または、開発環境上でビルドしたファイルをS3にデプロイする際には、s3 sync
などのコマンドを使うことが多いかと思います。
そんな時、Scottyが割と便利だったのでAngular CLIで作成したSPAをデプロイする前提で簡単に使い方を紹介します。
前提
- AWS credentialsが設定されている
- AWS CLI自体は必要無いがcredentialsが設定されている必要あり
- AWS CLI の設定 -AWS
- デプロイ先のS3バケットは作成済みである
- Node.js(v6.9以上)が使える
- Angular CLIがインストールされている
ひとまずやってみる
今回は、ng new
した直後のAnguarプロジェクトをng build
した結果生成されるファイルをScottyを使ってデプロイします。
Scottyをグローバルインストール
$ npm install -g scottyjs
ビルド
$ ng build --prod
scottyコマンドを叩きデプロイ
$ scotty --bucket scotty-angular-test --region ap-northeast-1 --spa --update --source ./dist
CloudFrontを作成しない場合
$ scotty --bucket scotty-angular-test --region ap-northeast-1 --spa --update --nocdn --source ./dist
※ 実行時のオプションパラメータはREADMEを参照
scottyコマンドを実行した後数分で、CloudFront ウェブディストリビューションの状態が有効になり、CloudFrontに設定されたURLでS3にでデプロイされたAngular SPAにアクセスできるようになります。
npm scriptsにビルド+デプロイコマンドを定義
上記の手順をnpm scriptsにまとめてしまいます。
{ "name": "scotty-test", "version": "0.0.0", "license": "MIT", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build --prod", "deploy": "npm run build && scotty --bucket scotty-angular-test --region ap-northeast-1 --spa --update --source ./dist", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, ...
また、Scottyがグローバルインストールされていない環境でも利用できるよう、devDependencyに登録しておきます。(この場合グローバルにインストールされている必要はない)
$ npm install --save-dev scottyjs
所感
現状S3への簡単なデプロイ方法として s3 sync
コマンドを使う方法があるけど、Scottyの場合、devDependencyでプロジェクトに追加して、npm scriptsから呼び出せるので別でビルド(orローカル開発)環境にAWS CLIが必要ないのがよい。
「手元でサクッとビルドしてとりあえず確認環境を構築したい!」といった用途に向いているかなぁという印象。
参考
Googleドキュメントを使って音声ファイルを文字起こしする小技
Google ドキュメントの音声入力機能を使い、mp3ファイルやブラウザ上でストリーミング再生した音声ファイルを文字起こしする方法(小技)を紹介します。
ドキュメント エディタ ヘルプ -Google
標準機能では、入力元の音声はマイク入力のみの対応となっていて、直接音声ファイルを入力することはできませんが、Soundflowerというフリーソフトを使い、音声ファイルをマイク入力として扱うことで音声を直接文字起こしすることができます。
必要なもの
手順
手順はいたって簡単です。
Soundflowerのインストール
今回はGitHubからdmgファイルをダウンロードしてインストールしました。 https://github.com/mattingalls/Soundflower/releases/tag/2.0b2
サウンド設定
Soundflowerのインストールが済んだら、システム環境設定
-> サウンド
を開き、「出力」と「入力」をそれぞれ Soundflower (2ch)
に変更します。
出力
入力
Googleドキュメントで使用する入力デバイスを選択する
Googleドキュメントを開き、ブラウザのアドレスバーの右端に表示されるビデオマークをクリックして、入力デバイスを選択します。
(Google Chromeの場合)
音声ファイルを再生する
文字起こしをしたい音声ファイルを再生します。
※ この時、出力はSoundflowerになっているため、内蔵スピーカやイヤフォンから音声は聞こえません。
Googleドキュメントで音声入力
Googleドキュメントを開き、通常の音声入力を行う手順で、マイクをOnにします。
※ ウィンドウが非アクティブになると音声入力が停止されるので注意が必要です。
参考
https://support.google.com/docs/answer/4492226?hl=ja https://qiita.com/Sasakky/items/09e3bc1536f0569fc893
Angularで画像の遅延読み込み(ng-lazyload-image)
ng-lazyload-imageを使って、スクロールに応じた画像の遅延読み込みをしてみる。
今回はわかり易い例として、スクロールに応じてグリッド配置した画像を例にした。
レスポンシブのグリッドを作るために、AngularMaterialの grid-list
も使っています。
デモ: AngularLazyloadGridimageDemo
ng-lazyload-imageの導入
まずは、Angularプロジェクトにインストール
$ npm install ng-lazyload-image --save
app.moduleにインポートしておく。
(別途モジュール分割している場合は、インポートする先が変わってくる)
app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { LazyLoadImageModule } from 'ng-lazyload-image'; // 追加 import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, LazyLoadImageModule // 追加 ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
グリッド画像に遅延ローディングを設定する
テンプレート側では、<img>
タグのlazyLoadディレクティブに画像URLを指定します。
src属性の代わりに、lazyLoadを使うだけなので、使い方はいたって簡単です。
app.component.html
<mat-grid-list cols="4" rowHeight="1:1"> <mat-grid-tile *ngFor="let item of range(150)"> <img lazyLoad="assets/images/nattouIMGL3800_TP_V.jpg"> </mat-grid-tile> </mat-grid-list>
また、lazyloadの文脈からは外れますが、サンプルの画像をリピートするためのメソッドは、次の記事を参考にAngular 2以降で動作するようにしています。
AngularJS tips - ng-repeat で配列ではなく数値で for ループする方法
app.component.ts
... export class AppComponent { range(n): number[] { const arr = []; for (let i = 0; i < n; ++i) { arr.push(i); } return arr; } }
ここまでの手順で、スクロールに応じて画像を遅延読込することが出来るのですが、「ふわっ」と徐々に画像を表示させるアニメーションを加えるためのCSSを追加します。
ng-lazyloaded
というクラスは、ng-lazyload-imageによって画像が完全に読み込まれたタイミングで動的に付加されるクラスです。
app.component.css
img { width: 100%; height: 100%; object-fit: cover; /* lazyload */ transition: opacity 1s; opacity: 0; } img.ng-lazyloaded { opacity: 1; }
サンプルのリポジトリ
参考
https://www.pakutaso.com/20180132010nato.html
http://phiary.me/angularjs-ng-repeat-for-loop-with-numbers/
https://www.webcreatorbox.com/tech/object-fit
Angular Animationsを初めて使ってみた
Angularを使ったWebアプリを作り際も、CSSのアニメーションしか使わない場合が多かったのですが、Angular Routerと連携したアニメーションや複数の要素に対するアニメーションなどを実現するためにAngular Animationsを使ってみることにしました。
そもそも今まで触ったことなかったので、気にしてなかったけど、どうやらv4.2以上ではアニメーション関連が強化されているらしい。 今回紹介する内容では、v5.1.1を使っています。
今回は、単一のコンポーネントにごく単純なFade In-Outアニメーションをさせてみたいと思います。 具体的には次のようにぬるっと表示される詳細検索パネル(風)のものです。
事前準備
今回は、次のコマンドでサンプルのプロジェクトを作成しました。
$ ng new expansion-panel --inline-style --inline-template
また、app.moduleにBrowserAnimationsModule
をインポートしておきます。
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; ... imports: [ BrowserModule, BrowserAnimationsModule ], ...
検索ボックス
まずは検索ボックスです。
テンプレートには、input要素と、buttonを配置しているだけです。
「詳細」ボタンを押した際に、親のコンポーネントにボタンが押されたことを通知したため、Output
とEventEmitter
を使っています。
search.component.ts
import { Component, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-search', template: ` <div> <form> <input type="text"> </form> <button (click)="onClickPanelOpen()">詳細</button> </div> `, styles: [` :host { position: fixed; top: 50; z-index: 99; left: 50%; margin-left: -40%; width: 80%; } input { padding: 0; font-size: 1.3em; font-family: Arial, sans-serif; color: #aaa; border: solid 1px #ccc; margin: 0; height: 38px; width: 100%; background-color: #ffffff; } button { width: 40px; font-size: .3em; position: relative; top: -36px; left: 85%; } `] }) export class SearchComponent { @Output() panelOpen = new EventEmitter<boolean>(); private panelState = false; onClickPanelOpen() { this.panelState = !this.panelState; this.panelOpen.emit(this.panelState); } }
詳細検索パネル
こちらは、詳細検索パネル(風)です。今回は、アニメーションの例なので詳細検索に関する機能は省いていますが...
親のコンポーネントで見通しが良くなるよう別ファイルにしていますが、特にロジックを持たないコンポーネントです。
panel.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'app-panel', template: ` <p>詳細検索</p> `, styles: [` :host { position: fixed; top: 48px; z-index: 99; left: 50%; padding: 0; margin-left: -40%; width: 80%; height: 400px; border: 1px solid #cccccc; box-shadow: 0px 4px 4px -2px #cccccc; } p { text-align: center; margin-top: 180px; } `] }) export class PanelComponent { }
app.component
次にapp.componentです。これは、ng new
した際に作成されるものを編集して使っています。
アニメーションの定義もこのコンポーネントで行っています。
検索ボックスから受けたpanelOpen
イベントに対してonChangePanelState()
メソッドをバインドしています。
app.component.ts
import { Component } from '@angular/core'; import { trigger, state, transition, style, animate } from '@angular/animations'; @Component({ selector: 'app-root', template: ` <app-search (panelOpen)="onChangePanelState()"></app-search> <app-panel @panelOpenTrigger *ngIf="panelState"></app-panel> `, styles: [], animations: [ trigger('panelOpenTrigger', [ state('void', style({ opacity: 0.4 })), state('*', style({ opacity: 1 })), transition('* <=> *', animate('0.2s ease-in-out')) ]) ] }) export class AppComponent { panelState: boolean; onChangePanelState() { this.panelState = !this.panelState; } }
- テンプレート内の
@panelOpenTrigger
、triggerのpanelOpenTrigger
- テンプレート内にトリガーを定義してanimationsのtriggerと関連付けを行っています。
- state('void', style({ opacity: 0.4 })),
- アニメーション前の初期状態のstyleを定義しています。
state()
の第一引数void
はコンポーネントが表示されていない状態を表しています。
- アニメーション前の初期状態のstyleを定義しています。
- state('*', style({ opacity: 1 })),
- アニメーション後のstyleを定義しています。
*
はコンポーネントが表示された際の状態を示しています。
- アニメーション後のstyleを定義しています。
- transition(' <=> ', animate('0.2s ease-in-out'))
transition()
メソッドの第一引数には上記のstateに定義した状態の移り変わりを定義しますが、今回は* <=> *
で全ての状態の移り変わりをハンドリングするよう設定しています。今回の場合、パネルを開く時、閉じるときで同様のアニメーションを実行します。animate()
メソッドで実際のアニメーションを定義しています。ここでは、0.2秒かけてstyleがstate()
で定義した状態に変化します。ease-in-out
はイージングの種類です。
所感
Angular CDKのExpansion Panelでもそれぽいことができたかもしれない。
そもそもこれぐらいならCSSだけでも出来るかもしれないけど...Angular Animationsの取っ付きとしてはよかったかなと。
参考
AngularCLIで単一ファイルコンポーネント
AngluarCLIでComponentを作成する際にVue.jsの単一ファイルコンポーネントっぽく、1Component1ファイルとする方法を紹介します。
やってみる
コマンド実行時のオプションで指定
コンポーネント作成(デフォルト)
$ ng g c <コンポーネント名>
$ ng g c --inline-style --inline-template <コンポーネント名>
--inline-style
--inline-template
オプションを付けて実行することでcss、htmlの内容がtsファイルにインラインで記述されます。
※ gはgenerateのエイリアス
※ cはcomponentのエイリアス
.angular-cli.jsonに設定を記述
事前にプロジェクトの設定ファイルに記述しておく方法です。
... "defaults": { "styleExt": "css", "class": { "spec": false }, "component": {} } }
.angular-cli.json(単一ファイルコンポーネント)
... "defaults": { "styleExt": "css", "class": { "spec": false }, "component": { "inlineStyle": true, "inlineTemplate": true } } }
以上です。
確認
オプション指定、もしくは.angular-cli.jsonに設定を記述した状態でComponentを作成してみます。
作成されたコンポーネントを確認すると、一つのtsファイルにstyleとテンプレートがまとまっているのが分かるかと思います。
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-one-file', template: ` <p> one-file works! </p> `, styles: [] }) export class OneFileComponent implements OnInit { constructor() { } ngOnInit() { } }
参考
https://github.com/angular/angular-cli/wiki/generate-component