技術メモ

役に立てる技術的な何か、時々自分用の覚書。幅広く色々なことに興味があります。

pythonでゴールデンクロスデッドクロスを可視化(2)

前回の続き

前回のものを改良し、

長期移動平均線が上向きで短期移動平均が長期移動平均を下から上へクロスする場合、買い。(ゴールデンクロス
長期移動平均線が下向きで短期移動平均が長期移動平均を上から下へクロスする場合、売り。(デッドクロス
引用: http://fx.kakaku.com/fx/articleview/?no=21

の定義に対応させた。
長期移動平均線の増減がない場合(傾きが0の場合)も含めることにした。

コードとしては、
前回のコードの以下の部分を変えただけ。

< # ゴールデンクロス、デッドクロスを見つける
< cross        = sma5 > sma20
< d_sma20      = sma20.shift(1) - sma20
< golden       = (cross != cross.shift(1)) & (cross == True) & (d_sma20 <= 0)
< dead         = (cross != cross.shift(1)) & (cross == False) & (d_sma20 >= 0)
---
> #ゴールデンクロス、デッドクロスを見つける
> cross  = sma5 > sma20
> golden = (cross != cross.shift(1)) & (cross == True)
> dead   = (cross != cross.shift(1)) & (cross == False)

    【結果】
f:id:swdrsker:20180531003137p:plain

たしかに、前回の結果であった”長期移動平均が下向きの時にゴールデンクロスになっていたところ”がなくなっている。
    【前回の結果】
f:id:swdrsker:20180518013058p:plain

pythonで為替データを取ってきて移動平均、ボリンジャーバンド、ゴールデンクロス、デッドクロスを可視化

pythonで為替データを分析したり可視化してみたいと考えた。
今回は、リアルタイムでドル円相場を取ってきてローソク足をプロットし、その上に移動平均ボラティリティゴールデンクロスデッドクロスを可視化してみるというところまでやってみる。

データを取ってくる

まずはデータを取ってこないといけない。
以前はCSVファイルを取ってこれるサイトを紹介したけれど、やっぱりリアルタイムでもデータを取ってきたい。(為替の分足CSVデータが無料でダウンロードできるサイト - 技術メモ
そこで今回はOANDAのAPIを使うことにした。
調べた限りOANDAだけが無料でリアルタイムのデータを取れるAPIを出してくれているらしいからだ。
OANDAの登録からキーの取得までは色々なサイトで確認してほしい。

pythonでは、oandapyV20というラッパーを使えばうまく取ってこられるらしい。
The oandapyV20 REST-V20 API wrapper documentation — OANDA REST V20 API Wrapper 0.6.3 documentation

ひとまずサンプルコードを動かしてみる。
コードは下の参考サイトにも載せた ここ を参考にした。

必要なライブラリがなければインストールしておく

pip install pandas
pip install matplotlib
pip install seaborn
pip install oandapyV20

今回はデータの取得と可視化を別のプログラムで書いたのでいったん”candle.csv”としてCSV形式で保存することにした。
account_tokenには自分で取得したアクセストークンを入れてほしい。

import datetime
import pandas
from oandapyV20 import API
import oandapyV20.endpoints.instruments as oandapy

start        = datetime.datetime(year=2018,month=5,day=1)
minutes      = 150 # 150分取得
start        = start.strftime("%Y-%m-%dT%H:%M:00.000000Z")
access_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
api          = API(access_token = access_token, environment="practice")
request      = oandapy.InstrumentsCandles(instrument = "USD_JPY",
               params = { "alignmentTimezone": "Japan", "from": start, "count": minutes, "granularity": "M1" })
api.request(request)

filename = "candle.csv"
candle = pandas.DataFrame.from_dict([ row['mid'] for row in request.response['candles'] ])
candle['time'] = [ row['time'] for row in request.response['candles'] ]
candle.to_csv(filename)

土日に動かしていたので日付は固定してしまっているが、スタート時刻を変えればリアルタイムでも動かせる。
(*土日は為替相場が閉まってるからデータが取れない!!!)

移動平均線、ボリバンを可視化する

import pandas
import seaborn
import matplotlib.pyplot as plt
from matplotlib.finance import candlestick2_ohlc as plt_candle
seaborn.set_style("whitegrid")

# csvを読み込む
candle = pandas.read_csv('candle.csv')

# 移動平均とボラティリティを求める
window_size  = 5 #5分移動平均を求める
avg_move     = pandas.Series.rolling(candle.c, window = window_size).mean().dropna().reset_index(drop = True)
sigma        = pandas.Series.rolling(candle.c, window = window_size).std(ddof = 0).dropna().reset_index(drop = True)
sigma_plus1  = avg_move + sigma
sigma_plus2  = avg_move + sigma * 2
sigma_plus3  = avg_move + sigma * 3
sigma_minus1 = avg_move - sigma
sigma_minus2 = avg_move - sigma * 2
sigma_minus3 = avg_move - sigma * 3

candle = candle[window_size:].reset_index(drop = True)

# X軸の見た目を整える
# 時間だけを切り出すために先頭からの12文字目から取るようにしている
xticks_number = 15 #15分刻みに目盛りを書く
xticks_index = range(0,len(candle), xticks_number)
xticks_date = [candle.time.values[i][11:16] for i in xticks_index]

figure, ax = plt.subplots()
plt_candle( ax,
            opens = candle.o.values,
            highs = candle.h.values,
            lows = candle.l.values,
            closes = candle.c.values,
            width=0.6,
            colorup='#DC143C',
            colordown='#4169E1')
plt.plot(avg_move, color='#006400')
plt.plot(sigma_plus1, color='#3CB371')
plt.plot(sigma_plus2, color='#9ACD32')
plt.plot(sigma_plus3, color='#ADFF2F')
plt.plot(sigma_minus1, color='#3CB371')
plt.plot(sigma_minus2, color='#9ACD32')
plt.plot(sigma_minus3, color='#ADFF2F')
plt.xticks(xticks_index, xticks_date, rotation=80)
plt.show()

f:id:swdrsker:20180518012800p:plain

5分の移動平均を求めている。
移動平均線ボリンジャーバンドアルゴリズム的に簡単に言うと
それぞれ「過去N本分のローソク足終値の平均、標準偏差」のこと。
標準偏差は1σ、2σ、3σまで求めるのが普通だ。


ここがよくわからないかもしれない

candle = candle[window_size:].reset_index(drop = True)

これは、移動平均と実際のローソク足を合わせることをしている。
移動平均線は過去5分の平均を求めている事を意味するので、最初の4分は意味がないことになる。
5分の移動平均線に対しては4分ずらさないとローソク足が一致しないので、そもそもデータから取り除いてしまえということだ。

意外と手間取ったのが見た目に関してだった。
良い感じで15分毎くらいにX軸を出したかったがあんまり簡単にできる方法が見つからなかった。
xticksという関数を見つけて無理やり良い感じになるようにしたけれどもっといい方法があるかもしれない。
あと、seabornは素晴らしいのでとりあえず使っとけって感じはある。

ゴールデンクロスデッドクロスを可視化

import pandas as pd
import seaborn
import matplotlib.pyplot as plt
from matplotlib.finance import candlestick2_ohlc as plt_candle
seaborn.set_style("whitegrid")

# csvを読み込む
candle = pd.read_csv('candle.csv')

#移動平均を求める
small_window = 5 # 5分平均線を求める
big_window   = 20 # 20分平均線求める
sma5         = pd.Series.rolling(candle.c, window = small_window).mean()
sma20        = pd.Series.rolling(candle.c, window = big_window).mean()

candle = candle[ big_window: ].reset_index(drop=True)
sma5 = sma5[ big_window: ].reset_index(drop=True)
sma20 = sma20[ big_window: ].reset_index(drop=True)

#ゴールデンクロス、デッドクロスを見つける
cross  = sma5 > sma20
golden = (cross != cross.shift(1)) & (cross == True)
dead   = (cross != cross.shift(1)) & (cross == False)

#ゴールデンクロス、デッドクロスの位置をリスト化する
index_g = [i-1 for i, x in enumerate(golden) if x == True]
index_d = [i-1 for i, x in enumerate(dead) if x == True]

# X軸の見た目を整える
# 時間だけを切り出すために先頭からの12文字目から取るようにしている
xticks_number = 15 #15分刻みに目盛りを書く
xticks_index = range(0,len(candle), xticks_number)
xticks_date = [candle.time.values[i][11:16] for i in xticks_index]

figure, ax = plt.subplots()
plt_candle( ax,
            opens = candle.o.values,
            highs = candle.h.values,
            lows = candle.l.values,
            closes = candle.c.values,
            width=0.6,
            colorup='#DC143C',
            colordown='#4169E1')
plt.plot(sma5, color='greenyellow', label='sma5')
plt.plot(sma20, color='darkgreen', label='sma20')
plt.scatter(index_g, sma20[index_g], color='red', s=500, label="golden cross")
plt.scatter(index_d, sma20[index_d], color='blue', s=500, label="dead cross")
plt.xticks(xticks_index, xticks_date, rotation=80)
plt.legend()
plt.show()

f:id:swdrsker:20180518013058p:plain

ゴールデンクロスデッドクロスは5分と20分の移動平均に関するクロスを計算するようにした。
ここでは20分移動平均線を5分移動平均線が下から交差すればゴールデンクロス、上から交差すればデッドクロス、ということにしている。

注意しないといけないことは、こちらは20分の移動平均を使っていることだ。
つまりローソク足に関しては最初の19分、5分の移動平均線に関しては最初の14分が不要ということになる。
それがこの部分にあたる。

candle = candle[ big_window: ].reset_index(drop=True)
sma5 = sma5[ big_window: ].reset_index(drop=True)
sma20 = sma20[ big_window: ].reset_index(drop=True)

どうやらゴールデンクロスデッドクロスについて調べてみると

長期移動平均線が上向きで短期移動平均が長期移動平均を下から上へクロスする場合、買い。(ゴールデンクロス
長期移動平均線が下向きで短期移動平均が長期移動平均を上から下へクロスする場合、売り。(デッドクロス
引用: http://fx.kakaku.com/fx/articleview/?no=21

と書かれてるところもあり、これに従うならもうすこし手を加える必要があるかもしれない。
改良版 ⇒ pythonでゴールデンクロスデッドクロスを可視化(2) - 技術メモ


OANDAのAPIは結構便利そうだ。
課金(口座に25万円以上入金)さえすればトレードに関するリクエストなんかも送れるらしく、リアルタイムで分析しながらシステムトレードなんかもできるかもしれない。
完全自動売買のプログラムによる不労所得生活もきっと夢じゃない。

Pythonでコマンドライン引数を渡す方法3種類

Pythonコマンドライン引数を渡す方法は知る限り下記の3種類ある。
1. sys.argv
2. argparser
3. docopt

このうち、主に上の2つを紹介する。

sys.argv

最も簡単な方法。
コマンドラインの引数を配列のようにして扱う。

sample

import sys

print('script name : %s'%sys.argv[0])
print(sys.argv[1])
$ python parsers.py hello
script name : parsers.py
hello

使い方

sys.argsに配列としてコマンドライン引数が入っている。0番目はプログラムの名前に当たるので、1番目以降を引数として使うことになる。

argparser

sys.argvでは引数の個数も決めておかないといけないし順番もバラバラではいけない。
使い方がわからなくなったとしたら、ソースを読むしかない。
これに対してargparserではシェルのコマンドのように"--help"や"-h"を引数にすることでヘルプを表示させることができる。
また、引数の順番関係なくオプション指定で引数を渡すことができる(`-i hello -m 10`と`-m 10 -i hello`は同じ意味になる)
人に使ってもらうようなプログラムや大規模なプログラムを書くときはargparserを使うのが一番良いと思う。

sample

import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int,
                    nargs='+',help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
                    const=sum, default=max,help='sum the integers (default: find the max)')
parser.add_argument('--display',action='store_true')

args = parser.parse_args()
if(args.display):
    print('result :')
print(args.accumulate(args.integers))
$ python parsers.py 1 2 3 --display
result :
3

$ python parsers.py 1 2 3 --sum
6

$ python parsers.py -h
usage: parsers.py [-h] [--sum] [--display] N [N ...]

Process some integers.

positional arguments:
  N           an integer for the accumulator

optional arguments:
  -h, --help  show this help message and exit
  --sum       sum the integers (default: find the max)
  --display

使い方

大まかな使い方としては
1.パーサーを作る
2.引数を追加する
3.引数を解析する
という順番になっている。
詳しくは下のリンク先を見てもらうとして、
引数を追加するadd_parser()の引数だけ示しておく。

name または flags - 名前か、あるいはオプション文字列のリスト (例: foo や -f, --foo)。
action - コマンドラインにこの引数があったときのアクション。
nargs - 受け取るべきコマンドライン引数の数。
const - 一部の action と nargs の組み合わせで利用される定数。
default - コマンドラインに引数がなかった場合に生成される値。
type - コマンドライン引数が変換されるべき型。
choices - 引数として許される値のコンテナー。
required - コマンドラインオプションが省略可能かどうか (オプション引数のみ)。
help - 引数が何なのかを示す簡潔な説明。
metavar - 使用法メッセージの中で使われる引数の名前。
dest - parse_args() が返すオブジェクトに追加される属性名。

docopt

コメントにMarkdownドキュメントっぽく使い方を書いておけばそれをもとにオプション引数を作ってくれるという優れもの。
僕自身まだあまり馴染みがなく偉そうな事は書けないので、解説は下のリンクに譲る。
Pythonのdocopt使い方メモ - Qiita
Pythonのコマンドライン引数処理の決定版 docopt (あとJuliaに移植したよ) - りんごがでている