Kibana+elasticsearchによる株価データベース

January 29, 2021

株価サーバーpython

目次

はじめに

筆者は、無尽蔵サイトから株価データを入手し、株価データベースを構築しています。株価データベースといっても、単にLinuxファイルシステム上に銘柄毎の株価時系列データが入ったcsvファイルを生成しているだけで、これだけでは、何も気の利いたことはできません。

サーバ上に構築された株価データベースをWebブラウザから株価チャートとして可視化できたらいいなぁ~、と思いつつ、お手軽な実現方法を検討してみました。その結果、候補に上がったのがKibanaとelasticsearchによるソリューションです。

  • elasticsearch:時系列データを扱うに適したデータベース
  • Kibana:elasticsearchのデータを可視化するためのWebアプリ

という筆者の認識です。

elasticsearchは、全文検索エンジンfessのデータベースにも採用されており、テキストの扱いに強いと言われており、株価のような数値を扱うにはどうかなという不安はありますが、将来的に株価だけでなく、決算データや企業ニュースなんかもデータベースにぶち込んで、何か分析出来たらという思惑もあります。

早速、Kibanaとelasticsearchのインストールからはじめてみます。

Kibanaとelasticsearchのインストール

ディレクトリ構成

kibana/
    docker-compose.yml
    kibana/
        kibana.yml
    es-data/

【注意】es-dataのパーミッションを777にしておく必要がある。

Kibanaとelasticsearchをdocker-composeでインストールします。docker-compose.ymlファイルは、基本的には、fessの公式docker-composeファイルを取得し、そこからfessの部分を取り除いたものです。

docker-compose.yml
version: "3"

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
    environment:
      - discovery.type=single-node
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    ports:
      - 9200:9200
    volumes:
      - ./es-data:/usr/share/elasticsearch/data
  kibana:
    image: docker.elastic.co/kibana/kibana:7.6.2
    ports:
      - 5601:5601
    volumes:
      - ./kibana/kibana.yml:/usr/share/kibana/config/kibana.yml
    environment:
      - "I18N_LOCALE=ja-JP"

kibanaをサブディレクトリにインストール

kibanaをデフォルトでインストールした場合、
http://サーバ名:5601/
でkibanaにアクセスすることになるが、次のようにサブディレクトリ/kibanaでアクセスするように変更する。
http://サーバ名:5601/kibana

kibana/kibana.yml
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
server.basePath: "/kibana"
#server.rewriteBasePath: true

Kibanaの日本語化

docker-compose.ymlのenvironmentで指定

docker-compose.yml
  kibana:
    environment:
      - "I18N_LOCALE=ja-JP"

リバースプロキシの設定

/etc/nginx/conf.d/default.conf
server {
    location /kibana/ {
        proxy_pass http://localhost:5601;
        rewrite /kibana/(.*)$ /$1 break;
        access_log off;
    }

この設定により、
http://サーバ名:5601/kibana
ではなく、
http://サーバ名/kibana
でアクセスできるようになる。

firewallの設定

本サイトに公開した手順でファイアーウォール(awall)を設定している場合は、ポート9200, 9201, 9300番を開ける必要があります。

/etc/awall/optional/larkbox-policy.json
   {
      "//": "elasticsearch",
      "in": "LAN",
      "out": "_fw",
      "service":[ {"proto": "tcp", "port": "9200"},
                  {"proto": "tcp", "port": "9201"},
                  {"proto": "tcp", "port": "9300"}
                 ],
      "action": "accept"
    },

elasticsearch+Kibana起動

起動

$ docker-compose up -d

elasticsearchの動作確認

$ curl -X GET http://192.168.1.13:9200/
{
  "name" : "6223a3fcfd3f",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "GjRZPKXERGm47w-t5PSRQw",
  "version" : {
    "number" : "7.6.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
    "build_date" : "2020-03-26T06:34:37.794943Z",
    "build_snapshot" : false,
    "lucene_version" : "8.4.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

elasticsearchにデータ投入

日経平均株価の4本値時系列データを投入

データ形式

1001.csv
2021/01/21,28710,28846,28677,28757,1144470000000
2021/01/22,28580,28698,28527,28631,1217520000000
2021/01/25,28698,28822,28566,28822,1016450000000
2021/01/26,28696,28740,28527,28546,1070610000000

以下のpythonスクリプトでelasticsearchにデータ投入します。

その前に、elasticsearchパッケージを入れておく。

$ pip install elasticsearch
elasticsearch_uploader.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from datetime import datetime as dt
import pandas as pd
from elasticsearch import Elasticsearch

def generate_doc(row):
    doc = {
        'start_value': row[1],
        'end_value': row[4],
        'low_value': row[3],
        'high_value': row[2],
        'timestamp': dt.strptime(row[0], '%Y/%m/%d')
    }
    return doc

def uploader():
    df = pd.read_csv('1001.csv', header=None, encoding='UTF-8')
    es = Elasticsearch('http://elastic:changeme@192.168.1.13:9200')

    for row in df.iterrows():
        doc = generate_doc(row[1])
        type = '1001'
        res = es.index(index="stock", doc_type=type, id=str(row[0]), body=doc)
    return

def main():
    uploader()
    return

if __name__ == '__main__':
    main()

データが投入されたことを確認

$ curl -XGET 'http://localhost:9200/stock/1001/0?pretty=true'
{
  "_index" : "stock",
  "_type" : "1001",
  "_id" : "0",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "start_value" : 17325,
    "end_value" : 17409,
    "low_value" : 17219,
    "high_value" : 17540,
    "timestamp" : "2015-01-05T00:00:00"
  }
}

Kibanaで可視化

Kibanaにアクセス
http://サーバ名/kibana

「独りで閲覧」をクリック

k0

「Discover」をクリック

k1

インデックスパターン の作成

「新規データを確認」をクリック

k2

インデックスパターン の作成

インデックスパターン:stock
を指定し、「次のステップ」をクリック

k3

時間フィルターのフィールド名:時間フィルターを使用しない
を指定し、「インデックスパターンを作成」をクリック

k4

インデックスパターン:stockが作成された。

k5

「Visualize」をクリック

k6

「新規ビジュアライゼーションを追加」をクリック

k7

「折れ線」をクリック

k8

インデックスパターン「stock」をクリック

k9

メトリック、バケットを以下のように指定し、「▶」アイコンをクリック。

メトリック
Y軸
集約:平均
フィールド:end_value

バケット
X軸
集約:日付ヒストグラム
フィールド:timestamp
最低間隔:日ごと

k10

日経平均株価終値のグラフが完成。

おわりに

株価データをelasticsearchに投入し、Kibanaで可視化する流れの動作確認はとれたのだが、Kibanaの癖が筆者にはしっくりこず、ここから筆者が思い描く株価分析結果の可視化ツールに発展させていく道のりはかなり遠いように思える。

Kibanaにプラグインを入れてローソク足チャートを表示させた事例もあるようだが、筆者の技術力不足もあり、ちょっと敷居が高い。Kibanaは諦めてもう少しお手軽な方法を探そうと思う。


Written by questions6768 who lives in Uji, Kyoto.