LoginSignup
56
67

More than 5 years have passed since last update.

pythonでローソク足(candle chart)の描画

Last updated at Posted at 2017-03-15

matplotlib.financeでローソク足

データの作成

ランダムウォークで架空の為替チャートを作成します。

import numpy as np
import pandas as pd


def randomwalk(periods=None, start=None, end=None, freq='B', tz=None,
               normalize=False, name=None, closed=None, tick=1, **kwargs):
    """Returns random up/down pandas Series.

    Usage:
        ```
        import datetime
        randomwalk(100)  # Returns +-1up/down 100days from now.
        randomwalk(100, freq='H')  # Returns +-1up/down 100hours from now.
        randomwalk(100, ,tick=0.1 freq='S')  # Returns +-0.1up/down 100seconds from now.
        randomwalk(100, start=datetime.datetime.today())  # Returns +-1up/down 100days from now.
        randomwalk(100, end=datetime.datetime.today())
            # Returns +-1up/down back to 100 days from now.
        randomwalk(start=datetime.datetime(2000,1,1), end=datetime.datetime.today())
            # Returns +-1up/down from 2000-1-1 to now.
        randomwalk(100, freq='H').resample('D').ohlc()  # random OHLC data
        ```

    Args:
        periods: int
        start: start time (default datetime.now())
        end: end time
        freq: ('M','W','D','B','H','T','S') (default 'B')
        tz: time zone
        tick: up/down unit size (default 1)

    Returns:
        pandas Series with datetime index
    """
    if not start and not end:
        start = pd.datetime.today().date()  # default arg of `start`
    index = pd.DatetimeIndex(start=start, end=end, periods=periods, freq=freq, tz=tz,
                             normalize=normalize, name=name, closed=closed, **kwargs)
    bullbear = pd.Series(tick * np.random.randint(-1, 2, len(index)),
                         index=index, name=name, **kwargs)  # tick * (-1,0,1のどれか)
    price = bullbear.cumsum()  # 累積和
    return price
np.random.seed(1)  # ランダムステートのリセット。常に同じランダムウォークが出来上がる
rw = randomwalk(60*24*90, freq='T', tick=0.01)
rw.head(5)
2017-03-19 00:00:00    0.00
2017-03-19 00:01:00   -0.01
2017-03-19 00:02:00   -0.02
2017-03-19 00:03:00   -0.02
2017-03-19 00:04:00   -0.02
Freq: T, dtype: float64
rw.plot()

README_4_1.png

最小tick0.01円の1分足を30日分生成

df = rw.resample('B').ohlc() + 115  # 初期値は115円
df.head()
open high low close
2017-03-17 115.00 115.38 114.76 115.36
2017-03-20 115.37 115.49 115.03 115.15
2017-03-21 115.14 115.69 115.07 115.65
2017-03-22 115.66 116.22 115.64 116.21
2017-03-23 116.20 116.47 115.93 116.11

resampleメソッド使って平日のみの日足(オプション how='B')に直し、open, high, low, closeの4本値(ohcl)にまとめました。

df.plot()

README_8_1.png

4本値グラフは上のように見づらいので、ローソク足に直します。

参考1

参考: stack over flow - how to plot ohlc candlestick with datetime in matplotlib?

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.finance as mpf
from matplotlib import ticker
import matplotlib.dates as mdates
import pandas as pd

def candlechart(ohlc, width=0.8):
    """入力されたデータフレームに対してローソク足チャートを返す
        引数:
            * ohlc:
                *データフレーム
                * 列名に'open'", 'close', 'low', 'high'を入れること
                * 順不同"
            * widrh: ローソクの線幅
        戻り値: ax: subplot"""
    fig, ax = plt.subplots()
    # ローソク足
    mpf.candlestick2_ohlc(ax, opens=ohlc.open.values, closes=ohlc.close.values,
                          lows=ohlc.low.values, highs=ohlc.high.values,
                          width=width, colorup='r', colordown='b')

    # x軸を時間にする
    xdate = ohlc.index
    ax.xaxis.set_major_locator(ticker.MaxNLocator(6))

    def mydate(x, pos):
        try:
            return xdate[int(x)]
        except IndexError:
            return ''

    ax.xaxis.set_major_formatter(ticker.FuncFormatter(mydate))
    ax.format_xdata = mdates.DateFormatter('%Y-%m-%d')

    fig.autofmt_xdate()
    fig.tight_layout()

    return fig, ax

candlechart(df)
(<matplotlib.figure.Figure at 0x207a86dd080>,
 <matplotlib.axes._subplots.AxesSubplot at 0x207a6a225c0>)

README_11_1.png

参考2

参考: Qiita - Pythonでローソク足チャートの表示(matplotlib編)

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.finance as mpf
from matplotlib import ticker
import matplotlib.dates as mdates
import pandas as pd

fig = plt.figure()
ax = plt.subplot()

ohlc = np.vstack((range(len(df)), df.values.T)).T #x軸データを整数に
mpf.candlestick_ohlc(ax, ohlc, width=0.8, colorup='r', colordown='b')

xtick0 = (5-df.index[0].weekday())%5 #最初の月曜日のインデックス

plt.xticks(range(xtick0,len(df),5), [x.strftime('%Y-%m-%d') for x in df.index][xtick0::5])
ax.grid(True) #グリッド表示
ax.set_xlim(-1, len(df)) #x軸の範囲
fig.autofmt_xdate() #x軸のオートフォーマット

README_13_0.png

SMA(Simple Moving Average)の追加

import matplotlib.pyplot as plt
import matplotlib.finance as mpf
from randomwalk import *

fig = plt.figure()
ax = plt.subplot()

# candle
ohlc = np.vstack((range(len(df)), df.values.T)).T  # x軸データを整数に
mpf.candlestick_ohlc(ax, ohlc, width=0.8, colorup='r', colordown='b')

# sma
sma = df.close.rolling(5).mean()
vstack = np.vstack((range(len(sma)), sma.values.T)).T  # x軸データを整数に
ax.plot(vstack[:, 0], vstack[:, 1])

# xticks
xtick0 = (5 - df.index[0].weekday()) % 5  # 最初の月曜日のインデックス
plt.xticks(range(xtick0, len(df), 5), [x.strftime('%Y-%m-%d') for x in df.index][xtick0::5])
ax.grid(True)  # グリッド表示
ax.set_xlim(-1, len(df))  # x軸の範囲
fig.autofmt_xdate()  # x軸のオートフォーマット
plt.show()

README_15_0.png

import matplotlib.pyplot as plt
import matplotlib.finance as mpf

def sma(ohlc, period):
    sma = ohlc.close.rolling(period).mean()
    vstack = np.vstack((range(len(sma)), sma.values.T)).T  # x軸データを整数に
    return vstack

fig = plt.figure()
ax = plt.subplot()

# candle
ohlc = np.vstack((range(len(df)), df.values.T)).T  # x軸データを整数に
mpf.candlestick_ohlc(ax, ohlc, width=0.8, colorup='r', colordown='b')

# sma
sma5 = sma(df, 5)
sma25 = sma(df, 25)
ax.plot(sma5[:, 0], sma5[:, 1])
ax.plot(sma25[:, 0], sma25[:, 1])


# xticks
xtick0 = (5 - df.index[0].weekday()) % 5  # 最初の月曜日のインデックス
plt.xticks(range(xtick0, len(df), 5), [x.strftime('%Y-%m-%d') for x in df.index][xtick0::5])
ax.grid(True)  # グリッド表示
ax.set_xlim(-1, len(df))  # x軸の範囲
fig.autofmt_xdate()  # x軸のオートフォーマット
plt.show()

README_16_0.png

plotlyでローソク足

plotlyの練習

参考: Qiita - [Python] Plotlyでぐりぐり動かせるグラフを作る

初めてplotlyを使うので、やりかた

conda install plotly

でインストールして以下のようにインポートします。

アカウントを作る必要あるやらないやら情報がいろいろありますが、規制緩和されて、今では無料である程度やりたい放題みたいです。

import plotly as py
py.offline.init_notebook_mode(connected=False) 

適当なサンプルデータを作ります。

fo = [[2000,1190547,1.36],
    [2001,1170662,1.33],
    [2002,1153855,1.32],
    [2003,1123610,1.29],
    [2004,1110721,1.29],
    [2005,1062530,1.26],
    [2006,1092674,1.32],
    [2007,1089818,1.34],
    [2008,1091156,1.37],
    [2009,1070035,1.37],
    [2010,1071304,1.39],
    [2011,1050806,1.39],
    [2012,1037101,1.41],
    [2013,1029816,1.43],
    [2014,1003532,1.42],
    [2015,1005656,1.46]]
raw = pd.DataFrame(fo, columns=['year', 'births', 'birth rate'])
raw
year births birth rate
0 2000 1190547 1.36
1 2001 1170662 1.33
2 2002 1153855 1.32
3 2003 1123610 1.29
4 2004 1110721 1.29
5 2005 1062530 1.26
6 2006 1092674 1.32
7 2007 1089818 1.34
8 2008 1091156 1.37
9 2009 1070035 1.37
10 2010 1071304 1.39
11 2011 1050806 1.39
12 2012 1037101 1.41
13 2013 1029816 1.43
14 2014 1003532 1.42
15 2015 1005656 1.46
data = [
    py.graph_objs.Scatter(y=raw["births"], name="births"),
]
layout = py.graph_objs.Layout(
    title="title",
    legend={"x":0.8, "y":0.1},
    xaxis={"title":""},
    yaxis={"title":""},
)
fig = py.graph_objs.Figure(data=data, layout=layout)
py.offline.iplot(fig, show_link=False)

newplot1.png

data = [
    py.graph_objs.Bar(x=raw["year"], y=raw["births"], name="Births"),
    py.graph_objs.Scatter(x=raw["year"], y=raw["birth rate"], name="Birth Rate", yaxis="y2")
]
layout = py.graph_objs.Layout(
    title="Births and Birth Rate in Japan",
    legend={"x":0.8, "y":0.1},
    xaxis={"title":"Year"},
    yaxis={"title":"Births"},
    yaxis2={"title":"Birth Rate", "overlaying":"y", "side":"right"},
)
fig = py.graph_objs.Figure(data=data, layout=layout)
py.offline.iplot(fig)
#py.offline.plot(fig)

newplot2.png

操作方法

  • マウスオーバーで数値の表示
  • ドラッグで拡大
  • ダブルクリックで元のビューに戻る

為替チャート

参考: Qiita - Pythonでローソク足チャートの表示(Plotly編)

from plotly.offline import init_notebook_mode, iplot
from plotly.tools import FigureFactory as FF
init_notebook_mode(connected=True) # Jupyter notebook用設定

通常のプロット

candleチャートの関数が用意されているので、open, high, low, closeのデータが用意できればローソク足のグラフは簡単に作成できます。

ただし、平日のみの表示ができません。土日も表示されます。

fig = FF.create_candlestick(df.open, df.high, df.low, df.close, dates=df.index)
py.offline.iplot(fig)

newplot3.png

平日のみのプロット

参考先の方が平日のみのindexに直していました。

fig = FF.create_candlestick(df.open, df.high, df.low, df.close)

xtick0 = (5-df.index[0].weekday())%5 #最初の月曜日のインデックス
fig['layout'].update({
    'xaxis':{
        'showgrid': True,
        'ticktext': [x.strftime('%Y-%m-%d') for x in df.index][xtick0::5],
        'tickvals': np.arange(xtick0,len(df),5)
    }
})

py.offline.iplot(fig)

newplot4.png

指標の追加

def sma(data, window, columns='close'):
    return data[columns].rolling(window).mean()

sma5 = sma(df, 5)
fig = FF.create_candlestick(df.open, df.high, df.low, df.close, dates=df.index)

add_line = Scatter( x=df.index,  y=df.close,  name= 'close values',
                   line=Line(color='black'))
fig['data'].extend([add_line])

py.offline.iplot(fig, filename='candlestick_and_trace', validate=False)

newplot5.png

from plotly.graph_objs import *
fig = FF.create_candlestick(df.open, df.high, df.low, df.close, dates=df.index)
add_line = [Scatter(x=df.index, y=df.close.rolling(5).mean(), name='SMA5', line=Line(color='r')),
            Scatter(x=df.index, y=df.close.rolling(15).mean(), name='SMA15', line=Line(color='b')),
            Scatter(x=df.index, y=df.close.rolling(25).mean(), name='SMA25', line=Line(color='g'))]

fig['data'].extend(add_line)
fig['layout'].update({'xaxis':{'showgrid': True}})

py.offline.iplot(fig, filename='candlestick_and_trace', validate=False)

newplot6.png

SMA, EMA比較

新たなチャートの作成

np.random.seed(10)
ra = randomwalk(60*24*360, freq='T', tick=0.01) + 115
df1 = ra.resample('B').ohlc()
import plotly.graph_objs as go
fig = FF.create_candlestick(df1.open, df1.high, df1.low, df1.close, dates=df1.index)
add_line = [go.Scatter(x=df1.index, y=df1.close.rolling(75).mean(), name='SMA75', line=Line(color='r')),
            go.Scatter(x=df1.index, y=df1.close.ewm(75).mean(), name='EMA75', line=Line(color='b'))]

fig['data'].extend(add_line)
fig['layout'].update({'xaxis':{'showgrid': True}})

py.offline.iplot(fig, filename='candlestick_and_trace', validate=False)

newplot7.png

何故か移動平均がガタガタしているので、拡大してみます。

import plotly.graph_objs as pyg
from datetime import datetime

def to_unix_time(*dt):
    """datetimeをunix秒に変換
    引数: datetimeの入ったリスト
    戻り値: unix秒に直されたリスト"""
    epoch =  datetime.utcfromtimestamp(0)
    ep = [(i - epoch).total_seconds() * 1000 for i in list(*dt)]
    return ep

fig = FF.create_candlestick(df1.open, df1.high, df1.low, df1.close, dates=df1.index)
add_line = [pyg.Scatter(x=df1.index, y=df1.close.rolling(75).mean(), name='SMA75', line=Line(color='r')),
            pyg.Scatter(x=df1.index, y=df1.close.ewm(75).mean(), name='EMA75', line=Line(color='b')),
            pyg.Scatter(x=df1.index, y=df1.close.rolling(75).mean(), name='SMA75', mode='markers'),
            pyg.Scatter(x=df1.index, y=df1.close.ewm(75).mean(), name='EMA75', mode='markers')]

fig['data'].extend(add_line)  # プロットするデータの追加
fig['layout'].update(xaxis = {'showgrid': True,
                               'type': 'date',
                               'range':to_unix_time([datetime(2017,9,1), datetime(2018,1,1)])})  # レイアウトの変更

py.offline.iplot(fig, filename='candlestick_and_trace', validate=False)

newplot8.png

横軸に休日が描かれていますが、移動平均線データの休日の値はNaNです。
故に、金曜日と月曜日を結ぶ直線ができてしまい、滑らかにスムージングしたはずの線がガタガタに見えます。

ちなみに参考の人がやっていたようにlayoutでxaxisを無理やり平日だけにするとSAM, EMAがプロットされなくなりました。
これは休日をなくすべくxaxisをstringとfloatに無理やり直す処理をしているから、SMAとEMAのindexとあわなくなったためでしょう。
SMA, EMAのindexもstring, float混合のindexにすれば平日のみxaxisにできなくもないでしょうが、今後時間足を任意の長さに変更することを想定していますので、無理にdatetime型を崩したくありません。

そのため休日が入って少し見づらいですが、plotlyのAPIをそのまま受け入れていきます。
平日だけのxaxisにする何かいい方法を知っている方がいたらコメントください。
まだまだplotlyのことよくわかっていないです。

Next
plotlyでキャンドルチャートプロット

56
67
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
56
67