2020-06-14

【TradingView】仮想通貨取引ストラテジを作る 6日目 「大きな取引所とのVolumeと価格の差を使う」

世界的な取引所と思うとbitFlyerやByBitはまだまだ小さな取引所だ。そこで大きな取引所とのVolumeと価格に差が生じた時の小さな値幅を狙う。

Article Image

Volume差と価格差

仮想通貨取引だけでなくあらゆる取引は取引所間で価格Volumeに差があるのは当然である。

今回はそれをつかって価格が収束しようとするモメンタムをキャッチしたいという狙いのもとストラテジを制作してみた。

その差をどうやって検出するのか

価格は単位が変わっても簡単に変更できるが、そもそもVolumeは取引所によってぜんぜん違うのでどのように差を出すのかという疑問がある。

もうおわかりだと思うがこれも「標準化」を使って差を検出する。

平均が0、分散が1となるようにデータを変換すること。正規化、基準化とも言う。サンプルデータの任意の値は、サンプルの平均値と標準偏差を用いて以下の式により標準化される。

標準化 | 統計用語集 | 統計WEB

これによりVolume値の桁が大きく違っても同じ土俵で(厳密な数字ではないが)比較が可能になる

ついでに価格も「標準化」で検出する(実際のUSDJPYの差は使わない)

この価格については取引所によって(なんらかの手数料やペアの関係で)恒常的に安いor高いが続くことも有るので絶対値で比較するより標準化したほうがより「一時的に高くなっているか安くなっているか」が比較しやすいと考えた。

コードは最下部に貼り付ける

本日も全公開。非常に長いので最下部へ移動。

bitFlyerでもByBitでも良い。1時間足。

ByBitに関してはデフォルトで取引所の差を計測する一覧に入れていない。

これはコード内にもあるが取引所がまだ小さいので一覧に入れるとノイズになると考える。実際にオンにするとPFが減少する。

また、前提としてBitMEXから流出した日本人の受け皿となっており、おおよそbitFlyerの値動きと同じ動きをするため差の検出元はbitFlyerを参照したほうが正確だと考えている。

なんかByBitのとこのコードが変?

(下部のコードを全部読む人は少ないと思うが...)

このストラテジの仮説はbitFlyer自体がそれほど大きな取引所でないという前提で、だからこそ「大御所の取引所と差が生まれやすい」という点を利用している。

ByBitもまだ小さな取引所なので取引所の平均値に混ぜて計測してしまうとノイズが増えるだけ。そこで「小さな取引所枠」としているbitFlyerを排除して変わりにByBitを差し込むという手法を取った。

とりあえずバックテストが見たい

いつも通り取引回数が少ないので実用には注意が必要である。

bitFlyer

image-20200613221644747

image-20200613221714251

ByBit

image-20200613221821785

image-20200613221830034

なぜかByBit:ETHUSDでも効く

image-20200613221906913

これもまたサンプル数の問題はあるが不思議。相関だろうか。

小刻みにトレードする

image-20200613222054767

ボリュームと価格の差で揺れるところを攻めるので、取る値幅は一瞬。

とは言え1時間足なので多少の滑りは加味できるが、手数料を設定すると・・・

image-20200613222150841

image-20200613222217209

bitFlyerでは5.171 から 3.213。結構痛手であることが分かる。

逆を言えばすぐにポジションを手放すので大きなDDも可能性が低いと言える。

ByBitで実験

BBでもbFの設定そのままを推奨しているが、bFを計測対象から排除してBB専用にするオプションが

image-20200613222431602

これを選択する(BitMEXはおまけ)

こうすると計測対象からbFが外れメインがBBになる。

image-20200613222513525

トレード回数が激減してしまった。

これは取引所ごとにVolumeの癖があるからだと思われる。つまり閾値を下回ってしまうパターンが多い。

image-20200613222620274

ここをマイナスへ下げてやるとより沢山トレードする(逆にプラスにすると減る)

image-20200613222723848

あらら。

ひどい有様である。

しかし、これはトレンドフォローではないので「取引所ごとにVolumeに対する価格の反応が違う」と考えると納得できないだろうか?

というわけで、お気づきかもしれないがこんなオプションがある。

image-20200613222823057

トレンドフォローなら禁断のコマンド。

ロングするところをショート。ショートするところをロングする。

ボックス相場で無限に負けるトレンドフォローを反転すると一時的に勝てるが、いきなり爆損するので通常はやってはいけない。

しかしこのストラテジはその例ではい(トレンドは関係ない)

オンにしてみよう。

image-20200613223208737

まぁ微妙である。

一応勝ったと言ってしまおう。

取引数が少なすぎるのは取引所の期間が短いからだろうか。

さいごに

本日も「取引数の少なさ」からロバストかどうかは疑問が残る結果となったが、このままはむとれへ投稿。

また先日の「とにかく明るい八つ墓村」もETHバージョンを作成して投稿。これでトータル何本だろう?忘れてしまった。

はむとれのような複合的に戦略を使ってトレードするタイプのBOTであればトレンドフォローにストラテジが偏っている事が多いと思うのでトレンドに一切左右されないこういうストラテジをエッセンスに加えて見るのもいいかもしれない。

コード公開

思ったより長い。気がついたら書いてた。

長くても完全無料。お金取るほどのストラテジではない。

おまけで下部のplotのコメントを外すとZ値が描画される。

//@version=4
//bitFlyer or ByBit 1h
strategy("まだ2勝しかしてないオオサンショウウオ", overlay=false,default_qty_type=strategy.cash, initial_capital=100000, default_qty_value=100000,currency="USD")

n = input(type=input.integer,defval=200 ,title="標準化に使用する期間")
entry_threshold = input(title="価格差が生じているかどうかの閾値", defval=4, step=0.1)
entry_volume_threshold = input(title="全取引所Volumeの平均がこの閾値以上でないと取引しない(-1 ~ 1ぐらいの範囲がベスト?)",defval=0,step=0.1)
base_broker = input(title="ベースにする取引所", defval="bitFlyer", options=["bitFlyer", "BitMEX", "ByBit"])
reverse_switch = input(false,title="取引を反転する:取引所によって癖が違うので反転は有効な場合がある")

//closeの比較
bitFlyer_fxbtcjpy_close = security("bitFlyer:FXBTCJPY", timeframe.period, close)
bitFlyer_fxbtcjpy_close_sma = sma(bitFlyer_fxbtcjpy_close,n)
bitFlyer_fxbtcjpy_close_Zscore = ( bitFlyer_fxbtcjpy_close - bitFlyer_fxbtcjpy_close_sma ) / stdev(bitFlyer_fxbtcjpy_close,n) //標準化

bybit_btcusd_close = security("ByBit:BTCUSD", timeframe.period, close)
bybit_btcusd_close_sma = sma(bybit_btcusd_close,n)
bybit_btcusd_close_Zscore = ( bybit_btcusd_close - bybit_btcusd_close_sma ) / stdev(bybit_btcusd_close,n) //標準化
//ByBitを使う場合はbitFlyerの代わりにByBitを差し込む
bitFlyer_fxbtcjpy_close_Zscore := base_broker == "ByBit" ? bybit_btcusd_close_Zscore : bitFlyer_fxbtcjpy_close_Zscore

bitmex_xbtusd_close = security("BitMEX:XBTUSD", timeframe.period, close)
bitmex_xbtusd_close_sma = sma(bitmex_xbtusd_close,n)
bitmex_xbtusd_close_Zscore = ( bitmex_xbtusd_close - bitmex_xbtusd_close_sma ) / stdev(bitmex_xbtusd_close,n) //標準化

binance_btcusdt_close = security("Binance:BTCUSDT", timeframe.period, close)
binance_btcusdt_close_sma = sma(binance_btcusdt_close,n)
binance_btcusdt_close_Zscore = ( binance_btcusdt_close - binance_btcusdt_close_sma ) / stdev(binance_btcusdt_close,n) //標準化

coinbase_btcusd_close = security("Coinbase:BTCUSD", timeframe.period, close)
coinbase_btcusd_close_sma = sma(coinbase_btcusd_close,n)
coinbase_btcusd_close_Zscore = ( coinbase_btcusd_close - coinbase_btcusd_close_sma ) / stdev(coinbase_btcusd_close,n) //標準化

kraken_btcusd_close = security("kraken:BTCUSD", timeframe.period, close)
kraken_btcusd_close_sma = sma(kraken_btcusd_close,n)
kraken_btcusd_close_Zscore = ( kraken_btcusd_close - kraken_btcusd_close_sma ) / stdev(kraken_btcusd_close,n) //標準化

//volumeの比較
bitFlyer_fxbtcjpy_volume = security("bitFlyer:FXBTCJPY", timeframe.period, volume)
bitFlyer_fxbtcjpy_volume_sma = sma(bitFlyer_fxbtcjpy_volume,n)
bitFlyer_fxbtcjpy_volume_Zscore = ( bitFlyer_fxbtcjpy_volume - bitFlyer_fxbtcjpy_volume_sma ) / stdev(bitFlyer_fxbtcjpy_volume,n) //標準化


bybit_btcusd_volume = security("ByBit:BTCUSD", timeframe.period, volume)
bybit_btcusd_volume_sma = sma(bybit_btcusd_volume,n)
bybit_btcusd_volume_Zscore = ( bybit_btcusd_volume - bybit_btcusd_volume_sma ) / stdev(bybit_btcusd_volume,n) //標準化
//ByBitを使う場合はbitFlyerの代わりにByBitを差し込む
bitFlyer_fxbtcjpy_volume_Zscore := base_broker == "ByBit" ? bybit_btcusd_volume_Zscore : bitFlyer_fxbtcjpy_volume_Zscore

bitmex_xbtusd_volume = security("BitMEX:XBTUSD", timeframe.period, volume)
bitmex_xbtusd_volume_sma = sma(bitmex_xbtusd_volume,n)
bitmex_xbtusd_volume_Zscore = ( bitmex_xbtusd_volume - bitmex_xbtusd_volume_sma ) / stdev(bitmex_xbtusd_volume,n) //標準化

binance_btcusdt_volume = security("Binance:BTCUSDT", timeframe.period, volume)
binance_btcusdt_volume_sma = sma(binance_btcusdt_volume,n)
binance_btcusdt_volume_Zscore = ( binance_btcusdt_volume - binance_btcusdt_volume_sma ) / stdev(binance_btcusdt_volume,n) //標準化

coinbase_btcusd_volume = security("Coinbase:BTCUSD", timeframe.period, volume)
coinbase_btcusd_volume_sma = sma(coinbase_btcusd_volume,n)
coinbase_btcusd_volume_Zscore = ( coinbase_btcusd_volume - coinbase_btcusd_volume_sma ) / stdev(coinbase_btcusd_volume,n) //標準化

kraken_btcusd_volume = security("kraken:BTCUSD", timeframe.period, volume)
kraken_btcusd_volume_sma = sma(kraken_btcusd_volume,n)
kraken_btcusd_volume_Zscore = ( kraken_btcusd_volume - kraken_btcusd_volume_sma ) / stdev(kraken_btcusd_volume,n) //標準化
 
base_Zscore = base_broker == "bitFlyer" ? bitFlyer_fxbtcjpy_close_Zscore 
  : base_broker ==  "ByBit" ? bybit_btcusd_close_Zscore : bitmex_xbtusd_close_Zscore
average_Zscore = (bitFlyer_fxbtcjpy_close_Zscore + bitmex_xbtusd_close_Zscore + binance_btcusdt_close_Zscore + coinbase_btcusd_close_Zscore + kraken_btcusd_close_Zscore - base_Zscore)  / 4

base_volume_Zscore = base_broker == "bitFlyer" ? bitFlyer_fxbtcjpy_volume_Zscore 
  : base_broker ==  "ByBit" ? bybit_btcusd_volume_Zscore : bitmex_xbtusd_volume_Zscore
  
average_volume_Zscore = (bitFlyer_fxbtcjpy_volume_Zscore + bitmex_xbtusd_volume_Zscore + binance_btcusdt_volume_Zscore + coinbase_btcusd_volume_Zscore + kraken_btcusd_volume_Zscore - base_volume_Zscore)  / 4

//plot
plot(average_Zscore / base_Zscore)
plot(average_volume_Zscore / base_volume_Zscore,color=color.red)

// plot(bitFlyer_fxbtcjpy_close_Zscore / base_Zscore, color=color.blue) //bF
// plot(bitmex_xbtusd_close_Zscore / base_Zscore, color=color.red) //BitMEX
// plot(binance_btcusdt_close_Zscore / base_Zscore, color=color.green) //Binance
// plot(coinbase_btcusd_close_Zscore / base_Zscore, color=color.orange) //Coinbase
// plot(kraken_btcusd_close_Zscore / base_Zscore, color=color.teal) //Kraken

if(average_volume_Zscore > entry_volume_threshold)

    if(average_Zscore / base_Zscore > entry_threshold)
        if(not reverse_switch)
            strategy.entry("Long",strategy.long)
        else
            strategy.entry("Short",strategy.short) //反転
    else if(average_Zscore / base_Zscore < entry_threshold * -1)  
        if(not reverse_switch)
            strategy.entry("Short",strategy.short)
        else
            strategy.entry("Long",strategy.long) //反転
    else
        strategy.close_all()
        
else
    strategy.close_all()


この記事のタグ

この記事をシェア


謎の技術研究部 (謎技研)