Edited at
JupyterDay 23

jupyter notebook上で金融データの描画・取得・操作

More than 1 year has passed since last update.

この記事はplotlyとpandasを結びつけるライブラリ"cufflinks"の紹介、およびそれを利用した金融関連のデータ描画、pandas_datareaderや自作モジュールによる金融データの取得、自作モジュールによる金融データの操作を行います。


データの描画


cufflinks 使い方

公式: GitHub - santosjorge/cufflinks


This library binds the power of plotly with the flexibility of pandas for easy plotting.

このライブラリーは簡単なプロットのために「plotlyの力」と「pandasの柔軟性」を結びつけます。


2017年12月現在、condaではインストールできません。


anaconda cloudで探すと(またはぐぐると) biocondaでRNAがなんとかとか言っている生物学系の別のcufflinksが見つかるけれども、全く関係ないものなので注意。


正しくインストールするにはpipを使って下さい。

$ pip install cufflinks


インポートと初期設定

cufflinksをインポートし、オフライン設定を有効化します。

import cufflinks as cf

cf.set_config_file(offline=True)

get_config_file()'offline': True,設定を確認します。

cf.get_config_file()

{'colorscale': 'dflt',

'datagen_mode': 'stocks',
'dimensions': None,
'offline': True,
'offline_link_text': 'Export to plot.ly',
'offline_show_link': True,
'offline_url': '',
'sharing': 'public',
'theme': 'pearl'}


動作確認

cf.datagen.box(20).iplot(kind='box')

Peek 2017-12-24 10-44.gif


ここ以降の画像は静止画像になっていますがgifをキャプチャするのが面倒だったのでjupyter notebook上ではマウスオーバー・ドラッグ可能です。


fig = cf.datagen.histogram(3).figure(kind='histogram')

cf.iplot(fig)

Screenshot-2017-12-17 cufflinks_practice.png


cufflinksによるローソク足のプロット


サンプルOHLCデータをプロット

np.random.seed(0)  # ランダム状態固定

df = cf.datagen.ohlcv() # ランダムなデータを作成
qf = cf.QuantFig(df)
qf.add_bollinger_bands() # ボリンジャーバンド追加
qf.iplot() # ローソク足プロット

Screenshot-2017-12-23 cufflinks_practice.png

qf.add_macd()  # MACD追加

qf.iplot()

Screenshot-2017-12-23 cufflinks_practice1.png

qf.add_sma([10,50],width=2, color=['red', 'green'])  # 移動平均線追加

qf.iplot()

Screenshot-2017-12-23 cufflinks_practice2.png


出来高も表示

qf = cf.QuantFig(df, legend='top')

qf.add_volume() # 出来高追加
qf.iplot()

Screenshot-2017-12-23 cufflinks_practice3.png

qf.add_shapes(hline=[80, -142], vline=[pd.Timestamp('20150317'), pd.Timestamp('20150218')])  # 価格80, -142に水平線

qf.iplot()

Screenshot-2017-12-23 cufflinks_practice4.png

qf.add_trendline('01Jan15', '13Feb15')  # トレンドラインの挿入

qf.iplot()

Screenshot-2017-12-23 cufflinks_practice5.png

日本人にはあまり馴染みのない'%d%b%y'形式しか受け付けないようです。

自分好みの形式で入力したいときはpandas.Timestampクラスとstrftimeメソッドを混ぜて下のようにしてやると打ち込みやすいと思います。

qf = cf.QuantFig(df, legend='top')

# 上と同様に2015年1月1日から2015年2月13日
start, end = (pd.Timestamp(x).strftime('%d%b%y') for x in ('2015/1/1', '2015-2-13'))
# mapを使うならこう
# `start, end = map(lambda x: pd.Timestamp(x).strftime('%d%b%y'), ('2015/1/1', '2015-2-13'))`
qf.add_trendline(start, end)
qf.iplot()

Screenshot-2017-12-23 cufflinks_practice6.png


データの取得


海外株

pandas_datareaderモジュールから米国株式のデータはダウンロードできます。

pandas-datareader


Functions from pandas_datareader.data and pandas_datareader.wb extract data from various Internet sources into a pandas DataFrame. Currently the following sources are supported:

pandas_datareader.dataとpandas_datareader.wbの関数はインターネット上の様々なデータをpandas DataFrameに抽出します。現在、以下のソースがサポートされています。


  • Yahoo! Finance

  • Google Finance

  • Enigma

  • Quandl

  • St.Louis FED (FRED)

  • Kenneth French’s data library

  • World Bank

  • OECD

  • Eurostat

  • Thrift Savings Plan

  • Nasdaq Trader symbol definitions


インストールは

$ conda install pandas-datareader

# または
$ pip install pandas-datareader

from pandas_datareader import data

end = pd.datetime.today()  # 今日の日付

start = (pd.Period(end, 'D')-300).start_time # 300日前日付
df = data.get_data_yahoo('AMZN', end=end, start=start) # 300日前からのAmazon株価取得

df.tail()




Open
High
Low
Close
Adj Close
Volume


Date










2017-12-11
1164.599976
1169.900024
1157.000000
1168.920044
1168.920044
2363500


2017-12-12
1166.510010
1173.599976
1161.609985
1165.079956
1165.079956
2235900


2017-12-13
1170.000000
1170.869995
1160.270020
1164.130005
1164.130005
2616800


2017-12-14
1163.709961
1177.930054
1162.449951
1174.260010
1174.260010
3156600


2017-12-14
1163.709961
1177.930054
1162.449951
1176.089966
1176.089966
1873348

qf = cf.QuantFig(df, legend='top', name='AMZN')

qf.add_volume()
qf.iplot()

Screenshot-2017-12-23 cufflinks_practice7.png


日本株

jsmモジュール利用して、日本株を取得してpandas DataFrameで返してくれるモジュールを組み立てました。

Qiita - 日本株の株価を取得してpandasデータフレームに格納する


参考

【Python/jsm】日本企業の株価データを銘柄ごとに取得


jsm 0.19


日本の株式市場の株価・財務データを取得するツールです。

各種データは、 Yahoo!ファイナンス からスクレイピングしています。


インストールは

$ pip install jsm

from read_nikkei import get_jstock  # 日本企業の株価取得モジュール

pandas_datareaderと異なる点


  • 銘柄はsymbol(Amazonなら'AMZN'とする)ではなく株価コード(数字4桁)で取得

  • 週足、月足での取得も可能(freq='W', freq='M'オプション)


    • デフォルトは日足(freq='D')



  • 期間の指定(periods=<数字>)で「endまたはstartから数えて<数字>本足の取得」


    • periodsを指定するとendまたはstartオプションが必ず必要です。



  • デフォルトでは、今日から数えて30日前からの日足データの取得(freq='D', end=pd.datetime.today(), periods=30と同様)


    • pandas_datereaderではデフォルトで2010年1月1日から今日までの株価取得(start=20100101, end=pd.datetime.today())



from read_nikkei import get_jstock

# 今日から100日前からの任天堂株価を取得
df = get_jstock(7974, end=pd.datetime.today(), periods=100)
df.tail()

    Get data from 2017-09-08 to 2017-12-17

/home/u1and0/.pyenv/versions/anaconda3-5.0.0/envs/snow/lib/python3.6/site-packages/bs4/__init__.py:181: UserWarning:

No parser was explicitly specified, so I'm using the best available HTML parser for this system ("lxml"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.

The code that caused this warning is on line 193 of the file /home/u1and0/.pyenv/versions/anaconda3-5.0.0/envs/snow/lib/python3.6/runpy.py. To get rid of this warning, change code that looks like this:

BeautifulSoup(YOUR_MARKUP})

to this:

BeautifulSoup(YOUR_MARKUP, "lxml")


なんかBeautifulSoup関連のワーニング出ますが、jsm作者に何とかしてもらうしかないんですかねー





Open
High
Low
Close
Adj Close
Volume


Date










2017-12-11
43800
44380
43400
44380
44380
1822300


2017-12-12
44380
44380
43710
43900
43900
1536600


2017-12-13
43430
44140
43100
43930
43930
2045600


2017-12-14
43700
43800
42800
43010
43010
2012200


2017-12-15
42500
43380
42320
43030
43030
2283300

qf = cf.QuantFig(df, legend='top', name='任天堂')

qf.iplot()

Screenshot-2017-12-23 cufflinks_practice8.png


為替

為替のデータはpandas_datereaderからでもある程度は取得可能です。

遡って1996年からの日足データを手に入れることができるようです。


日足

df = data.get_data_yahoo('JPY=X', start='1990')

df.head()




Open
High
Low
Close
Adj Close
Volume


Date










1996-10-30
114.370003
114.480003
113.610001
114.180000
114.180000
0.0


1996-10-31
NaN
NaN
NaN
NaN
NaN
NaN


1996-11-01
113.500000
113.500000
113.500000
113.500000
113.500000
0.0


1996-11-04
113.349998
113.980003
112.949997
113.879997
113.879997
0.0


1996-11-05
113.709999
114.330002
113.449997
114.250000
114.250000
0.0

qf = cf.QuantFig(df[-1000:], legend='top', name='USDJPY日足')  # 最後から1000本足をプロット

qf.iplot()

Screenshot-2017-12-23 cufflinks_practice9.png


1分足

1分足を扱うにはMetaTraderなどで扱われるヒストリカルファイル(.hst)をダウンロードしてくると利用できます。

MT4,5を使っている人はPCの中のどこかに.hst拡張子のファイルがあると思うので探して下さい。


参考: MT4でデモ口座・ライブ口座のヒストリカルデータを取得する方法


MT4,5を利用していない人はFXDD TRADINGなどからダウンロードして下さい。


wgetやaria2を使える環境にある人は

shell-session

$ wget http://tools.fxdd.com/tools/M1Data/USDJPY.zip

$ aria2c -x10 -s10 -k1M http://tools.fxdd.com/tools/M1Data/USDJPY.zip


2005年からの主要通貨1分足をhstファイルを圧縮したzip形式でダウンロードできます。容量の目安は50MB程度、zip解凍すると200MB程度です。


hstファイルをpandas DataFrameに変換

hst形式のままでは使えませんので、hstファイルをpandas DataFrame形式に変換するコードを書きました。

Qiita - u1and0/MT4ヒストリカルデータをpython上で扱えるようにしたりcsvに保存する

zip解凍から行ってくれますので、ダウンロードしたファイルはzip解凍しておく必要はありません。

from read_hst import read_hst

df = read_hst('/home/u1and0/Data/USDJPY.zip')

df = df[-1000:]
df.head()

Extracting USDJPY.hst...




open
high
low
close
volume


time









2017-11-16 15:57:00
113.027
113.030
113.001
113.001
77.0


2017-11-16 15:58:00
113.003
113.008
113.002
113.003
70.0


2017-11-16 15:59:00
113.003
113.010
113.000
113.001
72.0


2017-11-16 16:00:00
113.001
113.015
112.996
113.015
78.0


2017-11-16 16:01:00
113.015
113.027
113.010
113.025
89.0

qf = cf.QuantFig(df, name='USDJPY分足')

qf.add_volume()
qf .iplot()

Screenshot-2017-12-23 cufflinks_practice10.png


データ操作


ローソク足OHLCの時間足変換

参考: qiita - u1and0/ローソク足OHLCの時間足を変える

ohlcデータの時間軸を変えます。

def _ohlcv(columns, open=None, high=None, low=None, close=None, volume=None):

"""Make a dictionary of {open:'***', low:'***', high:'***', close:'***', volume:'***'}"""
auto_dict = {str(v).lower(): v for v in columns} # Lower case of columns dictionary
my_dict = {'open': open, 'high': high, 'low': low,
'close': close, 'volume': volume} # User defined OHLCV
updater = {k: v for k, v in my_dict.items() if v} # Remove `None` values in `my_dict`
auto_dict.update(updater) # Swap `auto_dict` with `my_dict` except `None` value
return auto_dict

def ohlc2(self, open=None, high=None, low=None, close=None, volume=None):
"""`pd.DataFrame.resample(<TimeFrame>).ohlc2()`
Resample method converting OHLC to OHLC
"""

auto_dict = _ohlcv(self.asfreq().columns, open, high, low, close, volume)
# Make dict as `agdict` for `df.resample(<Time>).agg(<dict>)`
try:
agdict = {auto_dict['open']: 'first',
auto_dict['high']: 'max',
auto_dict['low']: 'min',
auto_dict['close']: 'last'}
except KeyError as e:
raise KeyError('Columns not enough {}'.format(*e.args))
# Add `volume` columns
if 'volume' in auto_dict:
agdict[auto_dict['volume']] = 'sum'
return self.agg(agdict)

# Add instance as `pd.DataFrame.resample('<TimeFrame>').ohlc2()`
resample.DatetimeIndexResampler.ohlc2 = ohlc2

df.resample('H').ohlc2().head()




open
high
low
close
volume


time









2017-11-16 15:00:00
113.027
113.030
113.000
113.001
219.0


2017-11-16 16:00:00
113.001
113.059
112.800
112.840
6319.0


2017-11-16 17:00:00
112.840
112.951
112.725
112.783
6509.0


2017-11-16 18:00:00
112.785
112.888
112.766
112.884
5264.0


2017-11-16 19:00:00
112.884
112.905
112.794
112.857
4129.0

qf = cf.QuantFig(df.resample('H').ohlc2(), name='USDJPY時間足')

qf.add_volume()
qf.iplot()

Screenshot-2017-12-23 cufflinks_practice11.png

日経平均のデータを取得して、調整後終値(Adjusted Closing Price, Adj Close)を終値として扱いたいときは、ohlc2の引数に与えてあげます。

df = data.get_data_yahoo('^N225')  # 日経平均日足の取得

df.head()




Open
High
Low
Close
Adj Close
Volume


Date










2010-01-04
10609.339844
10694.490234
10608.139648
10654.790039
10654.790039
104400.0


2010-01-05
10719.440430
10791.040039
10655.570313
10681.830078
10681.830078
166200.0


2010-01-06
10709.549805
10768.610352
10661.169922
10731.450195
10731.450195
181800.0


2010-01-07
10742.750000
10774.000000
10636.669922
10681.660156
10681.660156
182600.0


2010-01-08
10743.299805
10816.450195
10677.559570
10798.320313
10798.320313
211800.0

df.resample('W').ohlc2().head()  # 普通に週足に変換




Open
High
Low
Close
Volume


Date









2010-01-10
10609.339844
10816.450195
10608.139648
10798.320313
846800.0


2010-01-17
10770.349609
10982.099609
10729.860352
10982.099609
963000.0


2010-01-24
10887.610352
10895.099609
10528.330078
10590.549805
872000.0


2010-01-31
10478.309570
10566.490234
10198.040039
10198.040039
779500.0


2010-02-07
10212.360352
10438.410156
10036.330078
10057.089844
794700.0

df.resample('W').ohlc2(close='Adj Close').head()  # 終値をAdj Closeに指定




Open
High
Low
Adj Close
Volume


Date









2010-01-10
10609.339844
10816.450195
10608.139648
10798.320313
846800.0


2010-01-17
10770.349609
10982.099609
10729.860352
10982.099609
963000.0


2010-01-24
10887.610352
10895.099609
10528.330078
10590.549805
872000.0


2010-01-31
10478.309570
10566.490234
10198.040039
10198.040039
779500.0


2010-02-07
10212.360352
10438.410156
10036.330078
10057.089844
794700.0

ohlc2()の引数にはopen, high, low, close, volumeを与えることができます。

例えば次のようにカラム名が4本値と関係ないデータを渡しても、きちんと引数を指定してやればOHLCVで変換してくれます。

np.random.seed(19)

df = cf.datagen.ohlcv() # データ生成
df.columns = range(1,6) # カラム名を数字に変える
df.head()




1
2
3
4
5




2015-01-01
100.000000
106.526092
77.530418
81.491630
8015


2015-01-02
83.748302
85.962681
63.060215
65.605544
5769


2015-01-03
65.592938
76.415738
47.096441
56.526309
9328


2015-01-04
58.160297
64.929405
52.899053
57.952424
4412


2015-01-05
58.498021
69.737244
34.999141
41.165273
6892

週足に変更するため、open, high, low, close, volumeをそれぞれ1,2,3,4,5と指定します。

df.resample('W').ohlc2(open=1, high=2, low=3, close=4, volume=5)  # 引数にohlcvのカラム名を指定




1
2
3
4
5




2015-01-04
100.000000
106.526092
47.096441
57.952424
27524


2015-01-11
58.498021
98.722285
25.081057
85.380938
34592


2015-01-18
85.227185
116.629094
56.120302
104.003280
35803


2015-01-25
103.913949
139.336095
82.181452
109.151731
38910


2015-02-01
108.334060
126.237090
75.235135
101.461833
39578


2015-02-08
101.868035
131.537039
27.324298
57.099236
33278


2015-02-15
55.782685
98.734416
49.267611
61.573598
34527


2015-02-22
62.830502
104.472611
48.834591
85.477329
30116


2015-03-01
84.665842
123.096526
44.282473
121.435018
40203


2015-03-08
120.073562
154.933346
120.073562
123.635981
55421


2015-03-15
123.368637
130.713508
54.363323
74.063469
50965


2015-03-22
72.393416
130.067377
61.799581
119.121722
35717


2015-03-29
120.383713
213.487056
111.342313
202.147684
36784


2015-04-05
202.951959
211.550772
102.660010
106.082669
42398


2015-04-12
106.244248
115.505312
76.272411
106.781127
23787

ただし、カラム名がFalseになるもの(0, None, False, np.NaNなど)を指定すると失敗します。


平均足

def heikin_ashi(self, open=None, high=None, low=None, close=None):

"""Return HEIKIN ASHI columns"""
df = self.copy()
auto_dict = _ohlcv(df.columns, open, high, low, close)
df['hopen'] = (df[auto_dict['open']].shift() + df[auto_dict['close']].shift()) / 2
df['hclose'] = df[[auto_dict['open'],
auto_dict['high'],
auto_dict['low'],
auto_dict['close']]].mean(1)
df['hhigh'] = df[[auto_dict['high'], 'hopen', 'hclose']].max(1)
df['hlow'] = df[[auto_dict['low'], 'hopen', 'hclose']].min(1)
return df[['hopen', 'hhigh', 'hlow', 'hclose']]

pd.DataFrame.heikin_ashi = heikin_ashi

df = data.get_data_yahoo('^GSPC')  # S&P500指数の取得

df.tail()




Open
High
Low
Close
Adj Close
Volume


Date










2017-12-18
2685.919922
2694.969971
2685.919922
2690.159912
2690.159912
3724660000


2017-12-19
2692.709961
2694.439941
2680.739990
2681.469971
2681.469971
3368590000


2017-12-20
2688.179932
2691.010010
2676.110107
2679.250000
2679.250000
3241030000


2017-12-21
2683.020020
2692.639893
2682.399902
2684.570068
2684.570068
3273390000


2017-12-22
2684.219971
2685.350098
2678.129883
2683.340088
2683.340088
2399830000

普通にローソク足の表示

qf = cf.QuantFig(df, name='S&P500ローソク足', legend='top',

slice=(pd.Timestamp('20170901'), pd.Timestamp('20171220')))
qf.iplot()

Screenshot-2017-12-23 cufflinks_practice12.png

平均足の表示は平均足メソッドを使用します。

he = df.heikin_ashi()

he.tail()




hopen
hhigh
hlow
hclose


Date








2017-12-18
2668.219971
2694.969971
2668.219971
2689.242432


2017-12-19
2688.039917
2694.439941
2680.739990
2687.339966


2017-12-20
2687.089966
2691.010010
2676.110107
2683.637512


2017-12-21
2683.714966
2692.639893
2682.399902
2685.657471


2017-12-22
2683.795044
2685.350098
2678.129883
2682.760010

qf = cf.QuantFig(he, name='S&P500平均足', legend='top',

slice=(pd.Timestamp('20170901'), pd.Timestamp('20171220')))
qf.iplot()

Screenshot-2017-12-23 cufflinks_practice13.png

ローソク足と比べて陽線、陰線が続きやすくなっています。


まとめ


  • グラフの描画



    • plotlypandasをの合わせ技をやりやすくしてくれるcufflinks



  • データの取得


    • 海外株、主要通貨、主要指数 → pandas-datareader

    • 日本株 → jsm + 自作モジュールでpandas DataFrame化

    • 為替の1分足 → FXDDからhstファイルをダウンロード + 自作モジュールでpandas DataFrame化



  • データ操作


    • OHLCをOHLCに変換 → 自作ohlc2()メソッド

    • 平均足 → 自作heikinashi()メソッド