日本株の複数年分の日足データを取得し、単一のCSVに保存するまで(Python)

##はじめに
この記事は[SFC Advent Calendar 2019](https://qiita.com/advent-calendar/2019/sfc)の22日目の記事です。何について書くか悩みましたが、今回は最近書いたコードをリライトして記事にすることにしました。

##背景
近年、様々なソフトウェアやライブラリの発展に伴い、株価データの活用が個人でも行われるようになってきています。ところが、日本株の株価データは著作権法上の問題で扱いが難しいため(詳細は[こちら](http://yudypon.blog.jp/archives/20150112.html))、現時点で大量の株価データを無料で一括ダウンロードできるサービスは存在しません。

しかしながら、銘柄ごとに1年ずつなら日足データをダウンロードできるサービスなら幾つか存在します。例えば、[株式投資メモ](https://kabuoji3.com/)さんや[バフェット・コード](https://www.buffett-code.com/download)さんです。

これらのサービスは1年分の株価の分析をしたいときには大変便利なのですが、複数年の株価を分析したいときには大量のファイルを処理しなければならないため多少不便です。

そこで今回は、[株式投資メモ](https://kabuoji3.com/)さんからダウンロードした複数のCSVを結合させるコードを作成することで、複数年分の株価データの利用を可能にしてみましょう。また、PythonのライブラリであるPandasのDataFrameを用いることで、そのままPythonで分析を行う際にも使いやすいように設計しています。

##データのダウンロードについて
上記をお読みになった方で、「ダウンロード部分のコードはないのか？」と思われた方もいらっしゃると思います。その点に関して、もちろん自動化することは可能なのですが、株式投資メモさんのサイトにてスクレイピングや自動化が許可されているかどうかが不明であるため、本稿でのコードの紹介は見送ります。

その代わりに、[フリーランスに憧れて](https://free-free.site/)さんが紹介されている[こちらのコード](https://free-free.site/post-650/)を使用することでダウンロードの完全自動化が可能です。

また、その際に２点変更すべき点がありますので、以下に記します。

1.ファイルの保存場所について

`download_dir = 'C:\\Python\\'`となっているところを、`download_dir = './csv'`とすることをお勧めします。こうすることで、ダウンロードしたファイルをカレントディレクトリ直下のフォルダに配置することができます。

2.繰り返し処理について

`for i in range(year,2019):`となっているところを、2019年までのデータが取得したいのであれば`for i in range(year,2020):`、2020年までのデータが取得したいのであれば`for i in range(year,2021):`のように1年ずつ増やす必要があります。

##データの準備
まず最初に、上項などの方法を用いて[株式投資メモ](https://kabuoji3.com/)から特定の銘柄の株価データを複数年分ダウンロードし、保存します。
###注意点
・__データのダウンロードは必ず時間をかけて行うようにし、サーバーに負荷をかけないよう気をつけてください。__
・__データに漏れがあるとエラーになってしまうので、必ず必要年数分全てのデータをダウンロードしてください。__
・__ファイル名は変更しないでください。__
・__全てのファイルは1つのフォルダにまとめ、また、同じフォルダに複数の銘柄のデータを入れないようにしてください。__


##CSVからのデータの読み込み
ダウンロードしたcsvを読み込むコードを作成します。
###コード

```python
import pandas as pd
import codecs

def read_csv(file_name: str) -> pd.core.frame.DataFrame:
    '''
    kabuoji3.comからダウンロードしたcsvをファイル名[file_name]を指定して読み込み、整形してDataFrame[df]として返します。
    '''
    with codecs.open(file_name, 'r', 'Shift_JIS', 'ignore') as f:
        df = pd.read_csv(f)
    df.columns = df.iloc[0]
    df.drop(df.index[[0,1]],inplace=True)
    df.index = pd.to_datetime(df.index, format='%Y-%m-%d')
    df.index.name = "日付"
    return df
```

###解説
株式投資メモでダウンロードできるCSVの文字コードは`Shift_JIS`なので、通常の方法でCSVを読み込むと文字化けが起こる可能性があります。それを防ぐため、ここでは`codecs`を利用して文字コードを指定してファイルを開き、`pandas`の機能である`read_csv()`を用いてCSVを`DataFrame`として読み込んでいます。
また、株式投資メモでダウンロードしたCSVファイルはヘッダー部分に銘柄情報が含まれており、`columns`や`index`が正しく認識されないため、これらを直接指定した上でヘッダーを削除する処理を含めています。更に、`index`の`日付`列に対し、`to_datetime`処理を行うことで、`index`の日付をそのまま`datetime`型として扱うことができるようになっています。
最終的に出力されるのは1年分の株価データを含んだ`pandas`の`DataFrame`です。

##ファイルリストの読み込み
1つのフォルダにまとまっているCSVのファイル名のリストを作成すると共に、それぞれのファイルが何年の株価データを含んでいるかどうかを認識して取得できるようにします。
###コード

```python
from glob import glob

def get_price_data_by_year(year: int) -> pd.core.frame.DataFrame:
    '''
    フォルダ内のファイルリスト[FILES_DICT]から指定年[year]の株価csvのファイル名を参照し、読み込んでDataFrame[df]として返します。
    '''
    file_name = FILES_DICT[str(year)]
    df = read_csv(file_name)
    return df

if __name__ == "__main__":
    
    #ダウンロードしたCSVが入っているフォルダのディレクトリを指定（相対パスでも絶対パスでも可、フォルダ名の後に/*を忘れずに）
    CSV_FOLDER_DIRECTORY = './csv/*'

    #上記のCSV_FOLDER_DIRECTORYを元にCSVファイルの辞書FILES_DICTを自動で作成する部分（編集不要）
    FILES_DICT = {}
    files = glob(CSV_FOLDER_DIRECTORY)
    files.sort()
    for file_name in files:
        FILES_DICT[file_name[-8:-4]] = file_name
```

###解説
`CSV_FOLDER_DIRECTORY = './csv/*'`の部分でCSVがまとまっているフォルダを指定します。相対パスでも絶対パスでも指定は可能ですが、フォルダ名の後に/*を忘れずにつけるようにしてください。
その後の部分で、CSVファイルの辞書`FILES_DICT`を作成します。これはグローバル変数として定義し、関数の中から参照できるようにする必要があります。
株式投資メモでダウンロードできるCSVは、例えば`7203_2012.csv`のような名前で保存されています。この中から`2012`(年数)の部分を抽出し、これを`key`とし、ファイル名を`value`とする`Dictionary`を作成することで、特定の年の株価データを含むCSVのファイル名を簡単に参照できるようになります。

##データの結合(年指定)
指定年から指定年までの数年分の株価データを順に読み込んで結合し、1つのデータとして出力します。
###コード

```python
def create_historical_data(open: int,last: int) -> pd.core.frame.DataFrame:
    '''
    指定年[open]から指定年[last]までの株価データを読み込み、結合して1つのDataFrame[df]として返します。
    '''
    df = get_price_data_by_year(open)
    for i in range(int(open) + 1,int(last) + 1):
        df = pd.concat([df, get_price_data_by_year(i)])
    return df
```

###解説
指定年`open`から指定年`last`までの株価データを順に読み込み、得られた`DataFrame`を`pd.concat()`を用いて結合しています。最後に全てのデータが結合された`DataFrame`が出力されます。なお、指定年のファイルが存在しない場合、エラーが発生します。指定年数分のデータを予めダウンロードしているか確認してから実行するようにしてください。

##データの結合(年数指定)
実行日から遡ってちょうど指定年数分の株価データを作成し、1つのデータとして出力します。これは日付単位での出力となりますが、遡ってちょうど指定年数となる日付が取引所の営業日ではなかった場合は、その直後の営業日から現在までの株価データを含んだ`DataFrame`が出力されます。同じように、実行日が取引所の営業日ではなかった場合は、直前の営業日までのデータを含んだ`DataFrame`が出力されます。

###コード

```python
import datetime as dt
from dateutil import relativedelta

def create_historical_data_by_date(years: int) -> pd.core.frame.DataFrame:
    '''
    実行日から遡ってちょうど指定年数[years]分の株価データを作成し、1つのDataFrame[df]として返します。
    '''
    this_year = int(dt.datetime.now().year)
    df = create_historical_data(this_year - years,this_year)
    open = dt.datetime.now() - relativedelta.relativedelta(years=years)
    df = df[df.index >= open]
    return df
```

###解説
まず最初に`datetime`を利用して現在の年を取得し、指定年数`years`分の株価データを前出の`create_historical_data()`を用いて取得します。
次に、`relativedelta`を利用し、現在から遡ってちょうど指定年数となる日付を`datetime`型で取得しています。前のステップで取得した`DataFrame`の`index`には既に`datetime`型への変換を行っているので、比較演算子`>=`を利用することで簡単に`DataFrame`をフィルタリングし、指定年数分のデータを取り出すことができます。最終的に出力されるのは、指定年数分の株価データを含んだ`DataFrame`です。

##CSVへの書き出し(保存)
上項で利用したいデータを出力することができましたが、外部でこのデータを扱うにはファイルに保存する必要があります。ここでは例として、CSVへの保存方法を紹介します。

###コード

```python
if __name__ == "__main__":

    #指定年から指定年までの株価データを保存したい場合
    df = create_historical_data(2015,2019)
    df.to_csv("2015-2019.csv")

    #実行日から遡ってちょうど指定年数分の株価データを保存したい場合
    df = create_historical_data_by_date(5)
    df.to_csv("5years_price.csv")
```

###解説
まず、上項で作成した`create_historical_data()`や`create_historical_data_by_date()`などの関数を利用し、得られた結果を変数`df`に保存します。この`df`は、`pandas`の機能である`to_csv()`を用いて簡単にCSVとして書き出すことができます。なお、この時保存するファイル名を指定する必要があり、`to_csv()`の引数として、`""`で囲った文字列を渡します。これには、必ず語尾に`.csv`を含めるようにしてください。
なお、この時保存されるファイルの文字コードは`UTF-8`で、区切り文字は`,`になります。Excelで読み込む際は、`データ>テキストファイル`を選択し、文字コードと区切り文字を指定するようにしてください。

##コード(まとめ)
今回使用したコードは、[GitHub](https://github.com/shota4/jp_stock_price_data/blob/master/edit_price_data.py)にて公開しています。興味のある方は[こちら](https://github.com/shota4/jp_stock_price_data/blob/master/edit_price_data.py)からご確認ください。

##最後に
最後までお読みいただきありがとうございました。皆様の株式分析とクリスマスがより豊かになることを願っております。