【仮想通貨bot】 JavaScriptで作る仮想通貨自動取引bot 第8回 VIX戦術bot(4) ソースコード解説+TradingViewのスクリプト変換のコツ+予告?

【概要】

・GitHubにあげたソースコードの解説

・TradingViewのスクリプトをbotに実装するコツ

・次回予告?

前回はVIX戦術botのソースコードを公開し、バックテストと実際の注文のテストをしました。今回はこのソースコードを解説します。TradingViewのスクリプト(Pineスクリプト言語)を参照するので難しいと感じるかもしれませんが、基本的な概念を掴んでしまえば実装できると思います(JavaScriptの基本的な知識が必要です)

ソースコードはこちらにあげておきました。

では、さっそく見ていきましょう。まずTradingViewを開きます。

「スクリプト」のところから「Noro's VixFix + RSI Strategy v1.0」で検索してください。画像をクリックするとポップアップが表示されます。下の方にソースコードが出て来るのでこれを説明していきます。ちなみにこれはPineスクリプト言語というTradingView独自の言語によって実装されています。リファレンスはこちら https://jp.tradingview.com/study-script-reference/

では1つずつ見ていきます。

strategy(title = "Noro's VixFix + RSI Strategy v1.0", ..

strategy関数はTradingViewのおまじないだと思ってください。JavaScriptでは使いません。

// TradingView
...
pd = input(22, title="LookBack Period Standard Deviation High")
bbl = input(20, title="Bolinger Band Length")
mult = input(2.0, minval = 1, maxval = 5, title = "Bollinger Band Standard Devaition Up")
lb = input(50, title="Look Back Period Percentile High")
...

inputという関数が出てきました。これはチャートの「戦略投資テスター」で変更できる値を設定するための関数です。JavaScriptではこれらは基本的に設定ファイルに書くことになります。JavaScriptに翻訳すると下記のようになります。

// JavaScript
exports.vixStrategyConf = {
 pd: 22, // LookBack Period Standard Deviation High
 bbl: 20, // Bollinger Band Length
 mult: 2, // Bollinger Band Standard Devaition Up
 lb: 50, // Look Back Period Percentile High
 ...
}

次にストラテジーの本体を見ていきます。

// TradingView
wvf = ((highest(close, pd)-low)/(highest(close, pd)))*100 // (1)
sDev = mult * stdev(wvf, bbl) // (2)
midLine = sma(wvf, bbl) // (3)
 // JavaScript
 // (0)
 // ohlcを使いやすい形に変更
 const openRev = CwUtil.getOpens(ohlc).reverse();
 const highRev = CwUtil.getHighs(ohlc).reverse();
 const lowRev = CwUtil.getLows(ohlc).reverse();
 const closeRev = CwUtil.getCloses(ohlc).reverse();

 var wvf = []; // Williams Vix Fix
 // (1)
 for (var i = 0; i < VIX.lb; i++) {
   var closeMax = Math.max.apply(null, closeRev.slice(i, i + VIX.pd));
   wvf[i] = (closeMax - lowRev[i]) / closeMax * 100; 
 }
 // iの小さい方が新しいデータであることに注意
 // (2) 
 const sDev = VIX.mult * SD.calculate({period : VIX.bbl, values : wvf})[0]
 // (3)
 const midLine = SMA.calculate({period : VIX.bbl, values : wvf})[0]; // ただの平均
 

(今回はPine言語 => JavsScriptへの変換をメインに説明するので、計算式についてはとりあえずこういうものなんだなと思ってください。)

まずJavsScript側の(0)です。TradingViewでは時間的に新しい方から過去に遡るように見ていくので、取得した高値安値などもこの形式に変換します。なお、データはCryptoWatchから取ってきています。ここでは割愛しますが、気になる方はソースコードを読んで下さい。

次に(1)です。Pineスクリプトではhighest関数で、現在からpd本分の終値のうち、もっとも高いものを取得しています。注意してほしいのは、TradingViewのこの形式では、現在のデータのみを対象にしているのではなく、過去のデータについても同様に計算しているということです。実際、(2)の標準偏差を計算する関数 stdev は過去のデータも使っています。つまり、JavaScriptの方では、過去のデータを必要に応じて取得する必要があります。ですので、JavaScript側の(1)は配列の形式で1つずつ過去に振り返りながら計算するようになっています。

次に(2)です。stdev 関数は標準偏差を取得する関数です。これをJavaScriptで計算するために、「technicalindicators」というライブラリを使います。正常にインストールできていれば入っているはずです。

const SD = require('technicalindicators').SD; // 標準偏差
SD.calculate({period : VIX.bbl, values : wvf})

periodに対象期間、valuesにwvfの値の一覧を設定して、関数を実行します。

他にも便利な関数が色々あるので、詳しく知りたい方はドキュメントを見てもらえればと思います。

次に(3)です。smaの取得もtechnicalInducatorに関数が用意されているのでこれを使います。ちなみに、今回はシグナルの計算には現在の値しか使わないので、SMAはただの平均になります。

次にストラテジーの続きです。

//RSI
fastup = rma(max(change(close), 0), 7)
fastdown = rma(-min(change(close), 0), 7)
fastrsi = fastdown == 0 ? 100 : fastup == 0 ? 0 : 100 - (100 / (1 + fastup / fastdown))
 // Fast RSI
 var up = [];
 var down = [];
 for (var i = 0; i < VIX.lb; i++) {
   up[i] = Math.max(closeRev[i]-closeRev[i+1], 0); // 直前の終値との差分
   down[i] = -Math.min(closeRev[i]-closeRev[i+1], 0); // 直前の終値との差分
 }
 // TradingViewのRMAが出てきたら、WEMAを使う(か、自前で実装する)
 const fastUps = WEMA.calculate({period: 7, values: up.reverse()}); // Wilders Smoothing, ライブラリの都合上、時間の昇順に戻す
 const fastUp = fastUps[fastUps.length - 1]; // 最新のデータだけ取得
 const fastDowns = WEMA.calculate({period: 7, values: down.reverse()});
 const fastDown = fastDowns[fastDowns.length - 1];

change関数で終値の差分を計算しています。ここでも、changeは過去の値も計算するので、JavaScriptにする場合は計算結果は配列になることに注意してください。オリジナルのコードは一気にfastUpまで計算していますが、配列で取得したいのでまず中身(up)を計算します。

次に、rmaという関数にかけるのですが、これがちょっと面倒で、TradingView側では現在データから過去データに向かって計算するのですが、JavaScriptのWEMA.calculate関数では古い値から順に計算します。TradingViewで扱いやすい形式にしたので、この関数にかける際は一旦元の時系列順に戻します。ここではup.reverse() で元に戻しています。

また、最終的には現在のRMA(WEMA)だけ取得できれば良いので、fastUpには最新のデータのみ設定しています。technicalindicatorのこの形式は慣れるまでわかりづらいと思うので、https://www.npmjs.com/package/technicalindicators の Playgroundのところで色々試してみることをおすすめします。

このあとのストラテジーの途中も似た調子で進むので割愛します。最後に実際に売買のシグナルを出す部分です。

//Signals
up = (wvf >= upperBand or wvf >= rangeHigh) and fastrsi < limit and close < open
dn = (wvf >= upperBand or wvf >= rangeHigh) and fastrsi > (100 - limit) and close > open
exit = ((strategy.position_size > 0 and close > open) or (strategy.position_size < 0 and close < open)) and body > abody / 3
 var upSignal = (wvf[0] >= upperBand || wvf[0] >= rangeHigh) && (fastRsi < VIX.limit) && (closeRev[0] < openRev[0]);
 var downSignal = (wvf[0] >= upperBand || wvf[0] >= rangeHigh) && (fastRsi > (100 - VIX.limit)) && (closeRev[0] > openRev[0]);
 var exitSignal = false;
 if (position === 'LONG') {
   if (closeRev[0] > openRev[0] && body[0] > abody / 3) {
     exitSignal = true;
   }
 } else if (position === 'SHORT') {
   if (closeRev[0] < openRev[0] && body[0] > abody / 3) {
     exitSignal = true;
   }
 } 

exitの部分がややこしいと思います。position_size > 0 でポジションがロングであることを表しています。JavaScript側でもまとめて書くことが出来なくはないのですが、終値が始値より高い/安いという条件が異なるので、別々にしてあります。

長くなってきたので、今回のソースコードの解説はここまでにします。最後に、TradingViewのPineスクリプトを変換するコツとしては、

・基本的に配列を扱っているのだと考える

・データのインデックスが小さい方が最新のデータになることに注意する

・technicalindicatorとの形式の違いに気をつける

というあたりになると思います。

以上で今回実装したソースコードの最低限の説明をしましたが、結構慣れるまで難しいので、今後実装する別のストラテジーで徐々に感触を掴んで貰えればと思います。

なおここまで作成してきたbotは、注文が通らない場合などに考慮できていませんでした。この他にチャートの障害など対応すべきことが色々あります。次回はこの対策を施したbotを実装し、より安全なトレードができるようにしていきます。

【予告?】

今回難しかったと思いますが、ここまで読んでいただけた方には嬉しいかもしれないお知らせです。次回、次々回あたりからドテン君予知botを書いていく予定です。5万円で売ってるアレに乗っかるbotです。イメージとしては、このbotに乗っかればそのうち勝手にドテン君が発動してくれるようなものを考えています。ただ、まだアイデアの段階なので、本当に成功するのか?というところも含めて、これから検証していきます。

Twitterはこちらです。 https://twitter.com/tonacoin BitMEXのアフィリンクあるので踏んでくれると喜びます。 https://www.bitmex.com/register/1zrLfZ