machine learning for tradingの始め方

初投稿且つ無償につき、クオリティはご容赦ください(笑)
今回はこちらのStefan JansenのGithubを通じて、金融市場の定量分析を行います。
ワークフローは以下に通りです。投資に機械学習を応用するにあたっては、Predictionsまでで事足りますので、本マガジンでは、そこまでを中心に解説していきたいと思います。

画像1

自動執行したい投資家の方々は、その先の実務への応用もございますので、そこからもかなりのエンジニアリング力が要求されますが、手動で取引する分にはPredictionsまで出来れば自身の投資戦略にエッジを持たせることは可能だと思います。

本Githubレポジトリのインストール

git clone https://github.com/stefan-jansen/machine-learning-for-trading.git

インポート

import warnings
warnings.filterwarnings('ignore')from pathlib import Path
import requests
from io import BytesIO
from zipfile import ZipFile, BadZipFile

import numpy as np
import pandas as pd
import pandas_datareader.data as web
from sklearn.datasets import fetch_openml

pd.set_option('display.expand_frame_repr', False)

データセットの保存場所

DATA_STORE = Path('assets.h5')


データセットのダウンロード

次に /data/create_datasets.ipynb を参考にしてください。これは今回のプロジェクトで使用するためのデータセットを取得します。
今回は取得するデータセットは
・米国上場企業3000社ヒストリカルデータ
・S&P500 ヒストリカルデータ
・S&P500 構成銘柄リスト
・NASDAQ, AMEXとNYSEに上場している企業のメタデータ
・債券データ

米国上場企業3000社ヒストリカルデータ

Quandleを使用します。以下、Quandlに関する説明とデータ取得のためのインストラクションになります。

Quandlは、データセットを3000 USの株価、配当、分割で公開します。上場企業。 Quandlは商用提供をするため、サポートを中止することを決定しましたが、過去データは機械学習本のソリューションとして使用するには依然として役立ちます。現在のデータに独自のアルゴリズムを実装することを確認してください。
> 2018年4月11日以降、このデータフィードはQuandlコミュニティで積極的にサポートされなくなりました。このデータフィードは引き続きQuandlでホストしますが、投資や分析に使用することはお勧めしません。


1. Quandlの無料アカウントを作成してください。
2. DownloadからWIKI/PRICESの全データをダウンロードしてください。
3. Zipを解凍してください。
4. /data/ディレクトリに移してwiki_prices.csvに変更してください。
5. 以下のコードを実行して、高速化のためにHDFフォーマットに変更してください。詳細は [Chapter 02 on Market & Fundamental Data](../02_market_and_fundamental_data).
df = (pd.read_csv('wiki_prices.csv',
                parse_dates=['date'],
                index_col=['date', 'ticker'],
                infer_datetime_format=True)
    .sort_index())
 #保存 
with pd.HDFStore(DATA_STORE) as store:
   store.put('quandl/wiki/prices', df)


S&P500 ヒストリカルデータ

これはstooq.comからダウンロードしますが、1950年から2020年までのS&P500のヒストリカルデータになります。こちらのファイルは大きくないので、こちらからアップロードさせていただきます。
その他、直近10年分であればFREDから以下の方法でも取得出来ます。

 #FREDからSP500のヒストリカルデータをダウンロード 
df = web.DataReader(name='SP500', data_source='fred', start=2009).squeeze().to_frame('close')
##保存
with pd.HDFStore(DATA_STORE) as store:
   store.put('sp500/fred', df)

##以下のファイルをダウンロードした場合の保存先#読み込み
sp500_stooq = (pd.read_csv('^spx_d.csv', index_col=0,
                    parse_dates=True).loc['1950':'2019'].rename(columns=str.lower)) #保存 
with pd.HDFStore(DATA_STORE) as store:
   store.put('sp500/stooq', sp500_stooq)

S&P500 構成銘柄リスト

こちらはwikipediaから取得致します。

 #データ取得 
url = 'https://en.wikipedia.org/wiki/List_of_S%26P_500_companies'
df = pd.read_html(url, header=0)[0]
 #データ整形 
df.columns = ['ticker', 'name', 'sec_filings', 'gics_sector', 'gics_sub_industry',
             'location', 'first_added', 'cik', 'founded']
df = df.drop('sec_filings', axis=1).set_index('ticker')

NASDAQ, AMEXとNYSEに上場している企業のメタデータ

ここでは、上記の上場企業の財務諸表内の指標や時価総額等のメタデータを取得します。やや時間がかかります。こちらのデータに関しては、取得に時間がかかるため、/data/us_equities_meta_data.csvのファイル名で保存されております。

 #データ取得 
url = 'https://old.nasdaq.com/screening/companies-by-name.aspx?letter=0&exchange={}&render=download'
exchanges = ['NASDAQ', 'AMEX', 'NYSE']
df = pd.concat([pd.read_csv(url.format(ex)) for ex in exchanges]).dropna(how='all', axis=1)
df = df.rename(columns=str.lower).set_index('symbol').drop('summary quote', axis=1)
df = df[~df.index.duplicated()]
 #時価総額を数字に変換 
mcap = df[['marketcap']].dropna()
mcap['suffix'] = mcap.marketcap.str[-1]
mcap.suffix.value_counts()
mcap = mcap[mcap.suffix.str.endswith(('B', 'M'))]
mcap.marketcap = pd.to_numeric(mcap.marketcap.str[1:-1])
mcaps = {'M': 1e6, 'B': 1e9}
for symbol, factor in mcaps.items():
   mcap.loc[mcap.suffix == symbol, 'marketcap'] *= factordf["marketcap'] = mcap.marketcap

債券データ

債券データも取得しておきます。

securities = {'BAMLCC0A0CMTRIV'   : 'US Corp Master TRI',
             'BAMLHYH0A0HYM2TRIV': 'US High Yield TRI',
             'BAMLEMCBPITRIV'    : 'Emerging Markets Corporate Plus TRI',
             'GOLDAMGBD228NLBM'  : 'Gold (London, USD)',
             'DGS10'             : '10-Year Treasury CMR',
             }

df = web.DataReader(name=list(securities.keys()), data_source='fred', start=2000)
df = df.rename(columns=securities).dropna(how='all').resample('B').mean() #保存 
with pd.HDFStore(DATA_STORE) as store:
   store.put('fred/assets', df)

Stooqのデータ取得(第9章で使用)

1.Stooq Webサイトから、選択した資産クラス、市場、頻度の組み合わせの価格データをダウンロードします。
2.Webサイトで概説されている優先フォルダー構造を使用して、結果をstooqに保存します。 /data/freq/market/asset_class (例: /data/daily/us/nasdaq etfs)の構造を持っています。

stooq_path = Path('stooq') 
if not stooq_path.exists():
   stooq_path.mkdir()
market = 'us'
STOOQ_URL = 'https://static.stooq.com/db/h/'
data_url = f'd_{market}_txt.zip'
response = requests.get(STOOQ_URL + data_url).content
with ZipFile(BytesIO(response)) as zip_file:
   for i, file in enumerate(zip_file.namelist()):
       if not file.endswith('.txt'):
           continue
       local_file = stooq_path / file
       local_file.parent.mkdir(parents=True, exist_ok=True)
       with local_file.open('wb') as output:
           for line in zip_file.open(file).readlines():
               output.write(line)

日本株の取得も必要なので、market="jp"でも同じように一回実行してください。

シンボルを追加する。

metadata_dict = {
   ('jp', 'tse etfs'): 34,
   ('jp', 'tse stocks'): 32,
   ('us', 'nasdaq etfs'): 69,
   ('us', 'nasdaq stocks'): 27,
   ('us', 'nyse etfs'): 70,
   ('us', 'nyse stocks'): 28,
   ('us', 'nysemkt stocks'): 26
}
for (market, asset_class), code in metadata_dict.items():
   df = pd.read_csv(f'https://stooq.com/db/l/?g={code}', sep='        ').apply(lambda x: x.str.strip())
   df.columns = ['ticker', 'name']
   df = df.drop_duplicates('ticker').dropna()
   print(market, asset_class, f'# tickers: {df.shape[0]:,.0f}')
   path = stooq_path / 'tickers' / market
   if not path.exists():
       path.mkdir(parents=True)
   df.to_csv(path / f'{asset_class}.csv', index=False)    
'''
jp tse etfs # tickers: 317
jp tse stocks # tickers: 3,722
us nasdaq etfs # tickers: 186
us nasdaq stocks # tickers: 3,505
us nyse etfs # tickers: 1,025
us nyse stocks # tickers: 3,939
us nysemkt stocks # tickers: 297
'''

HDF5フォーマットで価格データを保存する。

def get_stooq_prices_and_tickers(frequency='daily',
                                market='us',
                                asset_class='nasdaq etfs'):
   prices = []
   
   tickers = (pd.read_csv(stooq_path / 'tickers' / market / f'{asset_class}.csv'))

   if frequency in ['5 min', 'hourly']:
       parse_dates = [['date', 'time']]
       date_label = 'date_time'
   else:
       parse_dates = ['date']
       date_label = 'date'
   names = ['ticker', 'freq', 'date', 'time', 
            'open', 'high', 'low', 'close','volume', 'openint']
   
   usecols = ['ticker', 'open', 'high', 'low', 'close', 'volume'] + parse_dates
   path = stooq_path / 'data' / frequency / market / asset_class
   print(path.as_posix())
   files = path.glob('**/*.txt')
   for i, file in enumerate(files, 1):
       if i % 500 == 0:
           print(i)
       if file.stem not in set(tickers.ticker.str.lower()):
           print(file.stem, 'not available')
           file.unlink()
       else:
           try:
               df = (pd.read_csv(
                   file,
                   names=names,
                   usecols=usecols,
                   header=0,
                   parse_dates=parse_dates))
               prices.append(df)
           except pd.errors.EmptyDataError:
               print('\tdata missing', file.stem)
               file.unlink()

   prices = (pd.concat(prices, ignore_index=True)
             .rename(columns=str.lower)
             .set_index(['ticker', date_label])
             .apply(lambda x: pd.to_numeric(x, errors='coerce')))
   return prices, tickers
# load some Japanese and all US assets for 2000-2019
markets = {#'jp': ['tse stocks'],
          'us': ['nasdaq etfs', 'nasdaq stocks', 'nyse etfs', 'nyse stocks', 'nysemkt stocks']
         }
frequency = 'daily'

idx = pd.IndexSlice
for market, asset_classes in markets.items():
   for asset_class in asset_classes:
       print(f'\n{asset_class}')
       prices, tickers = get_stooq_prices_and_tickers(frequency=frequency, 
                                                      market=market, 
                                                      asset_class=asset_class)
       
       prices = prices.sort_index().loc[idx[:, '2000': '2019'], :]
       names = prices.index.names
       prices = (prices
                 .reset_index()
                 .drop_duplicates()
                 .set_index(names)
                 .sort_index())
       
       print('\nNo. of observations per asset')
       print(prices.groupby('ticker').size().describe())
       key = f'stooq/{market}/{asset_class.replace(" ", "/")}/'
       
       print(prices.info(null_counts=True))
       
       prices.to_hdf(DATA_STORE, key + 'prices', format='t')
       
       print(tickers.info())
       tickers.to_hdf(DATA_STORE, key + 'tickers', format='t')

以上でstooqからのデータ取得は終わりになります。


まとめ

とりあえず、ここまで出来れば特徴量エンジニアリング等を始めることが出来ます。このマガジンでは、このレポジトリを頑張って解説していきますが、私サラリーマンにつき、更新日は約束出来ません、、、

この記事が気に入ったらサポートをしてみませんか?