スクリーンショット_2018-03-30_21

暗号通貨自動トレードシステム開発キャンプ#5:VIX戦術のソースコード解説

#bot開発キャンプ第5夜
#進化を続けるVIX戦術
#VIXのソースコード解説

//VIX戦術の復習
//VIX戦術について思うこと
//[練習版]AKAGAMIさんTwitterコード改良
//最後に

こんばんは、しゃしゃしゃしゃです。
bot開発キャンプ第5夜を進めて行こうと思います。今回のnoteでは前回紹介したVIX戦術のソースコードの解説をメインに進めていこうと思います。今夜は参加者の方々がご自身で戦術のアレンジができるようになるのを目標に進めていこうと思います(pythonです)。Pineスクリプト版もそのうち書きます。ごめんなさい。
どなたかが、TwitterでTickごとに再計算と注文ごとに再計算はなにが違うのかを質問してくださっていたと思うので回答します。以下のWikiをみるに
Tickごとに再計算→「各ローソク足の終値で注文を計算」
注文ごとに再計算→「始値で2注文、高値、安値でそれぞれ1注文の合計4注文」
するっぽいです(多分正確にはOHLCからエミュレータが算出した値動きにしたがってそれぞれ注文を出すんだと思います)。とりあえず1ローソク足で4回計算をしているみたいですね。ざっくりとしたバックテストならTickごとに再計算でいいと思いますよ。

https://docs.google.com/document/d/1sCfC873xJEMV7MGzt1L70JTStTE9kcG2q-LDuWWkBeY/edit?pli=1#

・VIX戦術の復習
 VIX戦術とは相場の価格変動の激しさを表す指標であるボラティリティ・インデックス(Volatility Index)を用いて相場の相場の転換点を見つける逆張り戦術です。VIXの定義は下記の通り。

((MAX_VALU - MIN_NOW)/MAX_VALU) * 100
MAX_VALU:指定期間の最高値
MIN_NOW:現在足の安値

単体で利用しても効果があるとされていたVIX戦術ですが、研究が進められて現在では様々な指標と組み合わせた派生戦術が続々と生まれています。

EXニート((((((((っ・ω・)っ25歳(@MBNeet25)さんは
VIXとRSIのEMA(指数平滑移動平均線)という従来通りの組み合わせに一目均衡表+平均足を使ったトレード(+愉悦指数?ていうのも使ってるのかな)を研究中

どーみ(@doooooo_mi)さんはMACDとADX←じつはよく理解していない。を使った戦術を公開。記事に設定方法などを記載しており、すぐにでも真似ができる様になっています。

よしナックルさん(@yoshinakkuru)は魔改造RCI3linesというRCI3linesを独自に調整したものを利用している様です。noteでも公開している様ですね。

そして、BTCのbotをここまで広めた第一人者と言っても過言ではないAKAGAMI(@kanakagami1978)さんはpython用のコードを公開していました。

・VIX戦術について個人的に思うこと。
 まずは仮想NISHI(@Nishi8maru)さんがVIXを公開、続々と改良案、特徴の研究が進む。これらのほとんどが無償で提供されています。もちろんその情報をただ鵜呑みにして利用して稼ぐことができたでしょう。ただ、今回のこのVIX戦術研究によって自分の頭で考えて検証をした人が増えたと感じています。NISHIさんもおっしゃっていますが情報のオープンソース化が進んでいますね。
是非ともこのまま、自分の頭を使って研究した成果を一部でも周りの人に還元する世界が広がって欲しいと思います。

・[python練習版]AKAGAMIさんTwitterコード改良
 では、そろそろbot開発をしていきましょうか。今回使うのは上で紹介したAKAGAMIさんのコード(ちょっと改良してますけど)
これはpython上でVIX指数を出すためのコードです。今回も例にもれずタブが3文字の空白に置換されているので4文字の空白になるように編集してください(お手数をかけてしまい申し訳ないですが・・・)。
pythonの実行環境の構築等は#3のキャンプに書いているのでそれをみてください。それでもわからなかったらGoogleさんに聞いてみてください。お金を払ってもいいって人はAKAGAMIさんのnoteを買ってください。やり方はいくらでもあるのでご自身にあったスタイルで導入してみてください。

import time
import ccxt
import sys
import json
import requests
from datetime import datetime
import numpy as np
import pandas as pd
from pandas import Series
import collections
import time

now = datetime.now()
now = int(time.mktime(now.timetuple()))  # 現在時刻 (UNIタイム)取得
# BitMEXの1分足OHLCVデータ取得101本文
r = requests.get(
 'https://www.bitmex.com/api/udf/history?symbol=XBTUSD&resolution=1&from=' + str(int(now) - 60 * 100) + '&to=' + str(now))
ohlcv = r.json()  # JSONからリストへ変換
low = np.array(ohlcv['l'])
high = np.array(ohlcv['h'])
close = np.array(ohlcv['c'])
np.set_printoptions(precision=4)  # print時に小数点以下4桁まで表示

# CM_Williams_Vix_Fix and Vix_Fix_inverse
#upper/lowerBand:wvfのボリンジャーバンド
#rangeHigh/Low:lb期間のwvf最大値
def vixfix(close, low, high,period = 22,bbl = 20, mult = 2.0,lb = 50,ph = 0.85, pl=1.01):  
   print('VixFix')
   period = period  # LookBack Period Standard Deviation High
   bbl = bbl  # Bolinger Band Length
   mult = mult  # Bollinger Band Standard Devaition Up
   lb = lb  # Look Back Period Percentile High
   ph = ph  # Highest Percentile - 0.90=90%, 0.95=95%, 0.99=99%
   pl = pl  # Lowest Percentile - 1.10=90%, 1.05=95%, 1.01=99%
   hp = False  # Show High Range - Based on Percentile and LookBack Period?
   sd = False  # Show Standard Deviation Line?
   # VixFix
   wvf = (pd.Series(close).rolling(period, 1).max() - low) / pd.Series(close).rolling(period, 1).max() * 100
   # VixFix_inverse
   wvf_inv = abs((pd.Series(close).rolling(period, 1).min() - high) / pd.Series(close).rolling(period,
                                                                                               1).min() * 100)
   sDev = mult * pd.Series(wvf).rolling(bbl, 1).std()  # 期間bblでのVixFixの標準偏差
   midLine = pd.Series(wvf).rolling(bbl, 1).mean()  # 期間bblでのVixFixの単純移動平均
   lowerBand = midLine - sDev  # VixFixボリンジャーバンド(下)
   upperBand = midLine + sDev  # VixFixボリンジャーバンド(上)(水色)
   rangeHigh = pd.Series(wvf).rolling(lb, 1).max() * ph  # lb期間のwvf最大値
   rangeLow = pd.Series(wvf).rolling(lb, 1).min() * pl  # lb期間のwvf最小値
   result = collections.namedtuple('result', 'wvf, wvf_inv, lowerBand, upperBand, rangeHigh,rangeLow')
   return result(wvf=wvf,wvf_inv=wvf_inv,lowerBand=lowerBand,upperBand=upperBand,rangeHigh=rangeHigh,rangeLow=rangeLow)

wvf = vixfix(close=close,low=low,high=high).wvf
wvf_inv = vixfix(close=close,low=low,high=high).wvf_inv
print(wvf)
print(wvf_inv)

では解説をしていきます。最初の[import]で始まる数行、これは他の人が開発した便利なモジュール・ライブラリを自分のプログラムで使うことができるようにするための処理です。
インポートの仕方・解説は→http://programming-study.com/technology/python-import/

この段階でエラーが出る人はモジュールのどれかがインストールされていない可能性があるのでpip などでインストールしましょう[pip インストール モジュール名]で検索してみてください、大概出てきます。

import ccxt
import sys
import json
import requests
from datetime import datetime
import numpy as np
import pandas as pd
from pandas import Series
import collections

 続いて、BitMEXの1分足OHLCVデータを101本分取得する処理です。ちなみにOHLCVデータとはOpen, High, Low, Close, Volumeのデータのことです。
 一番上の行で現在時刻の秒数を取得してnowに代入requests.get(~~)のところで60*100秒前から今までのOHLCVデータをくれ!ってリクエストをしています。中身をよくみてみると&from=' + str(int(now) - 60 * 100) という文字列が見えますよねfrom ~ [~から]って意味ですよね?その後ろに+ '&to=' + nowという文字列が見えますね?to~[~まで]つまりこの中身を書き換えてOHLCVデータを200本に変える場合は&from=' + str(int(now) - 60 * 100)の100を200に変えればいいだけですね。そのあと、ohlcv = r.json()って書いてありますね。これは何をしているのでしょうか?ohlcv = r.json()の前の行にprint(r)を次の行にprint(ohlcv)を挿入してみましょう。実行した結果を見てみればどんな処理なのかわかると思います。
そして、安値、高値、終値をそれぞれlow, high, closeという変数に代入。

now = datetime.now()
now = int(time.mktime(now.timetuple()))  # 現在時刻 (UNIタイム)取得
# BitMEXの1分足OHLCVデータ取得101本文
r = requests.get(
  'https://www.bitmex.com/api/udf/history?symbol=XBTUSD&resolution=1&from=' + str(int(now) - 60 * 100) + '&to=' + str(now))
ohlcv = r.json()  # JSONからリストへ変換
low = np.array(ohlcv['l'])
high = np.array(ohlcv['h'])
close = np.array(ohlcv['c'])
np.set_printoptions(precision=4)  # print時に小数点以下4桁まで表示

最後に今回のキモ、VIXの計算を行なっています。
period:過去何本分のローソク足を参考にするのか(今回は22に指定)
bbl:ボリンジャーバンドの期間(今回は20に指定)
mult:ボリンジャーバンドの偏差(今回は2.0に指定)→今回は2σなので95.45%の確率で値動きをすると想定される範囲を指定しています。3.0を設定すれば99.73%の確率で値動きが収まる範囲になります。「なんでそんなこと分かるの?」って方はhttps://mathwords.net/sigumakukanを見て見てください。式なんて追っかけたくないからイメージだけを教えろよって方でも図をみることで、σを大きくすればするほど指定範囲自体が広がるからその間で値動きする確率は上がるよねって話です。なんで確率まで出せるのかに興味を持った方は「正規分布 とは」みたいな検索ワードで調べてみてはいかがでしょうか。
lb:過去何本分までのVIXの最大値最小値をみるか。
ph:lb期間のVIXの最大値にかけることで、現在のVIXがどれくらい大きいものかを相対的に示すことができる。
pl:phの逆
wvf:恐怖指数(VIX指数)のこと、定義は上の「・VIX戦術の復習」を参照。pd.Series(close).rolling(period, 1).max()でやっていることはperiod期間のclose(終値)の最大値を計算しているだけです。
wvf_inv:愉悦指数(VIXインバース)の事、定義は「・VIX戦術の復習」の高値と安値をひっくり返して絶対値をとっただけ。これが大きければ大きいほど相場への楽観的な意見が増えているということなので天井を見極めるのに役立ちそうな指標ですね。

# CM_Williams_Vix_Fix and Vix_Fix_inverse
#upper/lowerBand:wvfのボリンジャーバンド
#rangeHigh/Low:lb期間のwvf最大値
def vixfix(close, low, high,period = 22,bbl = 20, mult = 2.0,lb = 50,ph = 0.85, pl=1.01):  
  print('VixFix')
  period = period  # LookBack Period Standard Deviation High
  bbl = bbl  # Bolinger Band Length
  mult = mult  # Bollinger Band Standard Devaition Up
  lb = lb  # Look Back Period Percentile High
  ph = ph  # Highest Percentile - 0.90=90%, 0.95=95%, 0.99=99%
  pl = pl  # Lowest Percentile - 1.10=90%, 1.05=95%, 1.01=99%
  hp = False  # Show High Range - Based on Percentile and LookBack Period?
  sd = False  # Show Standard Deviation Line?
  # VixFix
  wvf = (pd.Series(close).rolling(period, 1).max() - low) / pd.Series(close).rolling(period, 1).max() * 100
  # VixFix_inverse
  wvf_inv = abs((pd.Series(close).rolling(period, 1).min() - high) / pd.Series(close).rolling(period,
                                                                                              1).min() * 100)
  sDev = mult * pd.Series(wvf).rolling(bbl, 1).std()  # 期間bblでのVixFixの標準偏差
  midLine = pd.Series(wvf).rolling(bbl, 1).mean()  # 期間bblでのVixFixの単純移動平均
  lowerBand = midLine - sDev  # VixFixボリンジャーバンド(下)
  upperBand = midLine + sDev  # VixFixボリンジャーバンド(上)(水色)
  rangeHigh = pd.Series(wvf).rolling(lb, 1).max() * ph  # lb期間のwvf最大値
  rangeLow = pd.Series(wvf).rolling(lb, 1).min() * pl  # lb期間のwvf最小値
  result = collections.namedtuple('result', 'wvf, wvf_inv, lowerBand, upperBand, rangeHigh,rangeLow')
  return result(wvf=wvf,wvf_inv=wvf_inv,lowerBand=lowerBand,upperBand=upperBand,rangeHigh=rangeHigh,rangeLow=rangeLow)

wvf = vixfix(close=close,low=low,high=high).wvf
wvf_inv = vixfix(close=close,low=low,high=high).wvf_inv
print(wvf)
print(wvf_inv)

 以上になります。コードで分からないところがあったら、googleで検索して見ましょう「pd.Series(wvf).rolling(lb, 1).max()」なら[pandas Series rolling とは]みたいな感じです。あとは名前から想像して見て[Series 最大値]とかで検索するとかですね。
で、見つかったものを打ち込んで見て動かしてみる。エラーが出たらよっしゃ!エラー見つけたラッキーくらいに思ってエラー文をコピペして検索しましょう、大抵解決策が見つかります。

・最後に
 前回Pineスクリプトの解説しますって言っておいて蓋を開けたらpythonの解説になってしまって申し訳ないです。pythonからPineスクリプトへの移行作業はそんなに難しくないので、pythonとPineスクリプトを見比べてみて自分なりに書き換えて見てください。
bot開発キャンプ第5夜はこれにて終了です。noteは買うのもいいけど自分なりのアレンジ加えるともっとためになりますよ。

あと、UKIさんのhttps://twitter.com/blog_uki/status/978505055568510978を読んで自分がとっている戦略が順張りか逆張りかを考えてパラメータ設定すると結構違うと思います。

最後まで参加いただきありがとうございました。

2018/04/01追記
 どうやらpythonではstrftime('%s')に正式には対応していないようでしたので修正を行いました。

import time
を追加

now = datetime.now().strftime('%s')
を修正して
now = datetime.now()
now = int(time.mktime(now.timetuple()))
これにして
r = requests.get(
 'https://www.bitmex.com/api/udf/history?symbol=XBTUSD&resolution=1&from=' + str(int(now) - 60 * 100) + '&to=' + now)
を
r = requests.get(
 'https://www.bitmex.com/api/udf/history?symbol=XBTUSD&resolution=1&from=' + str(int(now) - 60 * 100) + '&to=' + str(now))





いいなと思ったら応援しよう!