PythonでYahoo!ファイナンスの時系列データをDBに登録する

# 目次
株のデータ収集についての記事一覧を[こちら](https://qiita.com/fjunya/items/dbe5ef36246c88acd983)に記載しております。

# 目的
- Yahoo!ファイナンスから取得した時系列データをDBに登録する

# 使用する言語、ライブラリ等
- Python3.7
- Django(DBの登録に使用)
- mysql

# 事前準備
※言語、ライブラリ等のインストール手順は省略しております。

- Djangoをインストールする
- Djangoにてプロジェクトとアプリケーションを作成する
- DB(mysql)を準備する。

# 実装
### DBスキーマの定義
models.pyを作成する。Djangoのmodels.pyの説明は[こちら](https://docs.djangoproject.com/ja/2.2/intro/overview/)をご参照ください。

```python:models.py
from django.db import models

class RawPrices(models.Model):
    code = models.IntegerField("銘柄コード")
    date = models.DateField("日付")
    open_price = models.IntegerField("始値")
    close_price = models.IntegerField("終値")
    high_price = models.IntegerField("高値")
    low_price = models.IntegerField("安値")
    volume = models.IntegerField("出来高")
    adjustment_close_price = models.FloatField("調整後終値")

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['code', 'date'], name='unique_code_dete'),
```
DBに適用する為に以下のコマンドを実行する。

```
python manage.py makemigrations
python manage.py migrate
```
### adminサイトの設定
Djangoのadminサイトの使用方法については[こちら](https://docs.djangoproject.com/ja/2.2/intro/tutorial02/#introducing-the-django-admin)をご参照ください。
admin.pyを作成する。

```python:admin.py
from django.contrib import admin
from .models import RawPrices

class RawPricesAdmin(admin.ModelAdmin):
    list_display = ('code', 'date', 'open_price', 'close_price', 'high_price', 'low_price', 'volume', 'adjustment_close_price')

admin.site.register(RawPrices, RawPricesAdmin)
```
### csvファイルからDBに取り込む
Djangoのカスタムコマンドを使用してDBに取り込む処理を実装します。Djangoのカスタムコマンドの使用方法については[こちら](https://docs.djangoproject.com/en/2.2/howto/custom-management-commands/)をご参照ください。

```python:csv_to_db.py
from django.core.management.base import BaseCommand
from django.conf import settings
from app.models import RawPrices
import csv
import glob
import os
import datetime
from django.db import IntegrityError
import logging
logger = logging.getLogger(__name__)

class Command(BaseCommand):
    help = ''

    def handle(self, *args, **options):
        self.generate_from_csv_dir()

    def generate_from_csv_dir(self):
        csv_dir = settings.CSV_DIR
        for path in glob.glob(os.path.join(csv_dir, "*.T.csv")):
            file_name = os.path.basename(path)
            code = file_name.split('.')[0]
            self.generate_price_from_csv_file(code, path)

    def generate_price_from_csv_file(self, code: str, csv_file_path):
        with open(csv_file_path, encoding="shift_jis") as f:
            reader = csv.reader(f)
            next(reader)  # 先頭行を飛ばす
            for row in reader:
                raw_prices = RawPrices(
                    code=code,
                    date=datetime.datetime.strptime(row[0], '%Y/%m/%d').date(),
                    open_price=int(row[1]),
                    close_price=int(row[4]),
                    high_price=int(row[2]),
                    low_price=int(row[3]),
                    volume=int(row[5]),
                    adjustment_close_price=float(row[6])
                )
                try:
                    raw_prices.save()
                except IntegrityError:
                    logger.info("(code={code}:{date})既に登録されております".format(code=code, date=raw_prices.date))
                    # 重複エラーが出た場合はfor文から抜ける
                    break
                except Exception as e:
                    logger.exception(e)
                    logger.error("(code={code}:{date})登録に失敗しました".format(code=code, date=raw_prices.date))
```
コマンドを実行してDBに登録する。

```
python manage.py csv_to_db
```
### 開発用サーバを起動してadminサイトを確認する
```
python manage.py runserver
```
http://127.0.0.1:8000/admin/にアクセスする。
以下のようにDBに登録されております。
<img width="1334" alt="Django_adminサイト.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149245/c7ceb5f0-726c-a86e-be07-255369de4534.png">