Rubyで作るビットコイン自動売買プログラムにボリンジャーバンドの判定を組み込む

この前Udemyの動画を見ながらビットコインの自動売買プログラムを作りました。

https://takapiroblog.com/%E3%83%93%E3%83%83%E3%83%88%E3%82%B3%E3%82%A4%E3%83%B3%E3%81%AE%E8%87%AA%E5%8B%95%E5%A3%B2%E8%B2%B7%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F%EF%BC%88bi

このプログラムに「ボリンジャーバンド」という投資判断の指標を追加してみました。

ボリンジャーバンドとは

ボリンジャーバンドはこのプログラムを作ったUdemy動画の最後の方で紹介されていた指標です。指標をもとにビットコインを買ったり売ったりしたらよりプログラムっぽくなりそうです。ボリンジャーバンドについては下記のサイトがわかり易かったです。

https://orekabu.jp/bollinger-band/

ボリンジャーバンドには1σ、2σ、3σの3本の線が上下にあり、特に2σのボリンジャーバンド内に株価が推移する確率は95%にもなるそう。

ボリンジャーバンド

この2σの下のバンドに近づいたらビットコインを買い、上のバンドに近づいたら売るみたいなプログラムが出来たらと思います。

ボリンジャーバンドの計算式は

プログラムに組み込むために詳しい計算式を探していると英語のサイトが見つかりました。よく分からないけどこれだろう。

  * Middle Band = 20-day simple moving average (SMA)
  * Upper Band = 20-day SMA + (20-day standard deviation of price x 2) 
  * Lower Band = 20-day SMA - (20-day standard deviation of price x 2)
  * Middle Band = 単純移動平均線
  * Upper Band = 単純移動平均線 + (標準偏差(σ) x 2) 
  * Lower Band = 単純移動平均線 - (標準偏差(σ) x 2)

MiddleBandが上の図でいう「移動平均線」、UpperBandが2σバンドの上、LowerBandが下なのかな。

計算に必要なのは「単純移動平均線」と「標準偏差(σ)」

単純移動平均線

価格の平均値です。現在価格を取得して、配列に格納し、その平均値を求める方法で計算できそうです。

  # 現在価格を取得
  price = get_price
  
  # 配列に追加
  prices.push(price)
  
  if prices.length > 20
    # 配列から一番古いものを削除
    prices.shift
    
    # 単純移動平均線
    middle_band = prices.mean

標準偏差(σ)

method.rbに以下を追加します。

class Array
  # 合計
  def sum
    reduce(:+)
  end
 
  # 平均
  def mean
    sum.to_f / size
  end
 
  # 分散
  def variance
    m = mean
    reduce(0) { |a,b| a + (b - m) ** 2 } / (size - 1)
  end
 
  # 標準偏差
  def standard_deviation
    Math.sqrt(variance)
  end
end

method.rbに追加したらbitcoin.rbで以下のようにして標準偏差が計算できます。

    # 標準偏差(σ)
    standard_deviation = prices.standard_deviation

ボリンジャーバンドもどきを算出

最終的に出来たbitcoin.rbファイルがこれ

require 'date'
require './method'

#値段を入れる配列
prices = []

# 現在価格
price = 0

# 注文数量
order_size = 0.01

# もうお金ない判定
stop_price = 25000

# 無限ループ開始
while(true)

  # 保有資産(JPY)
  jpy_amount = get_my_money("JPY")["amount"]
  # 保有資産(BTC)
  btc_amount = get_my_money("BTC")["amount"]

  # 現在価格を取得
  price = get_price
  
  # 配列に追加
  prices.push(price)
  
  if prices.length > 20
    # 配列から一番古いものを削除
    prices.shift
    
    # 単純移動平均線
    middle_band = prices.mean
    
    # 標準偏差(σ)
    standard_deviation = prices.standard_deviation
    
    # ボリンジャーバンド(σ)
    upper_bollinger_band = middle_band + standard_deviation
    lower_bollinger_band = middle_band - standard_deviation
    
    # ボリンジャーバンド(2σ)
    upper_bollinger_band_2 = middle_band + (standard_deviation * 2)
    lower_bollinger_band_2 = middle_band - (standard_deviation * 2)
    
    puts "now price : "  + price.to_s
    
    # 現在価格がボリンジャーバンドを下回ったら買い
    if (price <= lower_bollinger_band_2) && (jpy_amount > stop_price)
      puts "買い注文を発注します。"
      order("BUY", price, order_size)
      
    # 現在価格がボリンジャーバンドを上回ったら売り
    elsif (price >= upper_bollinger_band_2) && (btc_amount > 0.001)
      puts "売り注文を発注します。"
      order("SELL", price, order_size)
      
    end
    
    else
      p prices
  end
  
  sleep(1 * 60)
  
end

ダメなところ

そもそもちゃんとボリンジャーバンドが計算されてるかも分かんないんですが、それもりもダメなところに気づきました。

このプログラムは1分ごとにビットコインの価格を取得して動いてるんですが、これだと「20日移動平均線」どころか「20分移動平均線」という短すぎるスパンの役に立たないものになってます。。。

ビットフライヤーのAPIは過去の価格は取れないみたいなんで、ボリンジャーバンドは別の方法を考えないとなあ。