GASでビットコインの自動売買

おすすめ

Google Apps Script (GAS) でビットコインを自動売買する方法です。
仮想通貨取引所のbitFlyer(ビットフライヤー)のAPIを利用して、ビットコインの自動売買を行います。GASを利用すると無料で簡単に自動取引が行えます。完全な自動化ができるようにGASのスクリプトを用意しました。コピーして使えるので自己流にアレンジして自動取引を行いましょう。

1.自動売買の準備

(1) 前提事項

GASでビットコインの自動売買を行うために準備するものです。仮想通貨取引所のbitFlyer(ビットフライヤー)のAPIを使用するため、bitFlyerのアカウントが必要です。

前提事項
  • Google アカウント
  • bitFlyer アカウント(ビットフライヤー)

各アカウントがない場合、それぞれのサイトからアカウントを作成します。アカウントは無料で作成ができます。

GoogleのWebサイト

Google のWebサイトを開き「ログイン」のボタンをクリックします。
※Webサイト:https://www.google.com/

ログイン画面で「アカウントを作成」をクリックします。

アカウントの種類を選択

「自分用」を選択します。「ビジネスの管理用」は法人向けサービスGoogle Workspaceにて使用する場合に選択します。

メールアドレスを入力

名前とメールアドレス、パスワードを入力します。メールアドレスは既存のメールアドレスを登録(Gmail以外でも)できます。新しくGmailを作成する場合は「代わりに新しいGmailアドレスを作成する」をクリックして、取得したいメールアドレスを指定します。

電話番号・生年月日・性別を入力
利用規約に同意するとアカウントが作成される
bitFlyerのWebサイト

bitFlyer のWebサイトを開き「無料アカウント作成」のボタンをクリックします。
※Webサイト:https://bitflyer.com/

メールアドレスを入力し「無料で登録する」をクリックすると、指定のアドレスに本人情報の登録URLが通知されます。

本人情報の登録

アカウントを作成するため以下の情報の登録が必要となります。確認資料はスマートフォン等のカメラで撮影した写真をアップロードします。
本人情報の登録後、ビットコインを取り扱うbitFlyer口座が開設されます。

  • 本人情報(名前、住所、電話番号)
  • 取引目的(職業、年収)
  • 確認資料(運転免許証、パスポート、マイナンバーカードなど)
ハガキ確認と口座登録

本人確認内容のハガキが郵送されます。bitFlyer口座の入出金に使用する自分の銀行口座を別途登録します。bitFlyer口座から出金する場合はこの口座を使用します。

bitFlyer口座に入金

開設したbitFlyer口座に入金します。1万円~程度あればビットコインを購入可能です。

(2) bitFlyerでAPIを有効化

自動化を行うためにbitFlyerのAPIを有効にします。APIを有効にすることで取引画面を操作せずにビットコインの売買が可能になります。

APIサイトにログイン

bitFlyerにログイン後、APIのWebサイトを開きます。
Webサイト:https://lightning.bitflyer.com/

APIの追加

APIの画面で「新しいAPIキーを追加」ボタンをクリックします。

「新規注文を出す」のみにチェックを入れ「OK」をクリックします。他の項目にチェックがあっても問題はありませんが、自動取引で使用する項目のみに絞っています。

APIキーを作成後、「API Key」「API Secret」の項目を確認しておきます。「API Secret」は表示をクリックすると表示されます。この2つはスクリプトで使用します(後述)。

APIキーは機密情報。外部に漏洩しないよう注意

(3) GASの新規作成

Google Apps Script (GAS) を新規に作成します。GASはパソコンのブラウザで作成する必要があります。GASはGoogleドライブのファイルとして作成します。
名前を「ビットコイン自動売買」など任意の名前に変更しておきます。

Googleドライブを開く

Googleにログインして「ドライブ」を開きます。

Google Apps Scriptを新規作成

「Google Apps Script」がない場合、「+アプリを追加」から追加します。
アプリの追加画面で「Script」を検索し「Google Apps Script」を選択してインストールします。

2.スクリプトの設定

(1) bitFlyer用のスクリプトをコピー

次の「bitFlyer用スクリプト」をコピーして、新規作成したGASに貼り付けます。「bitFlyer用スクリプト」はbitFlyerのAPIを使用してビットコインの自動売買を行うスクリプトです。

/**
 * -------------------------------------------------------------------
 * bitFlyerクラス
 * -------------------------------------------------------------------
 */
class bitFlyer {
  constructor() {
    this.DEF_BASEURL = 'https://api.bitflyer.jp';
    this.DEF_VERSION = '/v1/';
    this.DEF_GETTICKER = 'getticker?product_code=';
    this.DEF_SENDORDER = 'me/sendchildorder';
    this.BITFLYER_API_KEY =  '';        // API有効化画面の API Keyを指定(必須)
    this.BITFLYER_API_SECRET =  '';     // API有効化画面の API Secretを指定(必須)
    this.BITFLYER_OPTION =  {
      product_code      : 'BTC_JPY',    // 注文するプロダクト
      child_order_type  : 'MARKET',     // 指値注文の場合は "LIMIT", 成行注文の場合は "MARKET"
      side              : 'SELL',       // 買い注文の場合は "BUY", 売り注文の場合は "SELL" 
      price             : 0,            // "LIMIT" を指定した場合は価格
      size              : 0.001,        // 注文数量(~0.001)
      minute_to_expire  : 300,	        // 期限切れまでの時間を分で指定
      time_in_force     : 'GTC'         // 執行数量条件 を "GTC", "IOC", "FOK"
    };
    this.BITFLYER_MAIL =  '';           // 取引通知(取引結果をメールする場合はアドレスを指定)
    this.BITFLYER_FEERATE = 0.07;       // 売買手数料(0.01~0.15% BTC)
    this.BITFLYER_ISORDER = false;      // 取引状態
    this.PREVIOUS_DATANAME = 'data.txt'; // 前回データ格納ファイル名
    this.PREVIOUS_DATACOUNT =  10;      // 前回履歴の保存件数
  }
  // Data storage methods
  Log() {
    var data = this.getData();
    Logger.log( this.getTradeMessage(data) );
  }
  getTradeMessage(data){
    if( data.p_side1 === '' ) {
      return "前回売買:" + data.ticker.toLocaleString() + " (" + data.side +") " +
             "→ 今回価格:" + data.p_ticker1.toLocaleString() + " (なし) " + data.p_message1 ;
    } else {
      return "前回売買:" + data.prevticker.toLocaleString() + " (" + data.prevside +") " +
             "→ 今回価格:" + data.p_ticker1.toLocaleString() + " (" + data.p_side1 + ")" + 
             data.message + " [ " + data.size + " " + data.product_code + " ]";
    }
  }
  sendOrderMail(para) {
    if( this.BITFLYER_MAIL != '' ) {
      var m_subject = '',m_body = '';
      if( 'error' in para ) {
        m_subject = "BTC自動売買[エラー]" ;
        m_body = JSON.stringify(para);
      } else {
        m_subject = "BTC自動売買[" + para.side + "]"; 
        m_body = this.getTradeMessage(para) + "\n\n" + JSON.stringify(para, null, 2);
      }
      MailApp.sendEmail( this.BITFLYER_MAIL, m_subject, m_body);  
    }
  }
  getData(){
    var data = JSON.parse(this.getDataFile(this.PREVIOUS_DATANAME));
    if( data ) {
      return data;
    } else {
      var obj = Object.assign({}, this.BITFLYER_OPTION );
      obj.ticker = 0;
      this.setDataOption(obj);
      return obj;
    }
  }
  setData( obj ){
    this.setDataOption(obj);
    if( 'side' in obj && 'ticker' in obj ) {
      obj.p_side1 =  obj.side;
      obj.p_size1 =  obj.size;
      obj.p_ticker1 =  obj.ticker;
      obj.p_message1 =  obj.message;
      this.setPreviousData(obj);
      var approxincome = 0,approxfee = 0;
      if( parseInt( obj.prevticker ) > 0 ) {
        if( obj.side === 'SELL' )
          approxincome = (parseInt(obj.ticker)*parseFloat(obj.size)) - (parseInt(obj.prevticker)*parseFloat(obj.prevsize));
        approxfee = (parseInt(obj.ticker)*parseFloat(obj.size)) * this.BITFLYER_FEERATE * 0.01;
      }
      obj.approxincome = parseInt(obj.approxincome) + parseInt(approxincome);
      obj.approxfee = parseInt(obj.approxfee) + parseInt(approxfee);
      obj.tradecount = parseInt(obj.tradecount) + 1;
    } else {
      Logger.log("CONSISTENCY ERROR:" + JSON.stringify(obj));
    }
    this.setDataFile(this.PREVIOUS_DATANAME,JSON.stringify(obj, null, 2));
  }
  setDataOption (obj) {
      obj.prevside = '';
      obj.prevticker = obj.prevsize = 0;
      obj.prevmessage = '';
      obj.tradecount = obj.approxfee = obj.approxincome = 0;
      for (var i = 1; i <= this.PREVIOUS_DATACOUNT; i++) {
        obj['p_side' + i ] =  '';
        obj['p_size' + i ] =  '';
        obj['p_ticker' + i ] = 0;
        obj['p_message' + i ] =  '';
      }
  }
  setPreviousData(obj) {
    var predata = this.getData();
    if( predata ) {
      for (var i = 1; i < this.PREVIOUS_DATACOUNT; i++) {
          if( ( 'p_side' + i ) in predata ) obj['p_side' + (i + 1) ] =  predata['p_side' + i];
          if( ( 'p_size' + i ) in predata ) obj['p_size' + (i + 1) ] =  predata['p_size' + i];
          if( ( 'p_ticker' + i ) in predata ) obj['p_ticker' + (i + 1) ] =  predata['p_ticker' + i];
          if( ( 'p_message' + i ) in predata ) obj['p_message' + (i + 1) ] =  predata['p_message' + i];
      }
      if( obj.p_side1 != '' ) {
        obj.prevside = predata.side;
        obj.prevsize = predata.size;
        obj.prevticker = predata.ticker;
        obj.prevmessage = predata.message;
        obj.approxincome = predata.approxincome;
        obj.approxfee = predata.approxfee;
        obj.tradecount = predata.tradecount;
      }
    }
  }
  // Trading methods
  setSendOrder(order_type = '',order_message = '') {
    this.BITFLYER_OPTION.side = order_type;
    var resultOrder = this.sendChildOrder();
    resultOrder.message = order_message;
    if( 'error' in resultOrder ) {
      Logger.log("ERROR:" + JSON.stringify(resultOrder));
    } else {
      this.setData(resultOrder);
    }
    this.sendOrderMail(resultOrder);
  }
  setNoOrder(order_message = '') {
    var obj = this.getData();
    obj.p_side1 = obj.p_size1 = '';
    obj.p_ticker1 = this.getTicker();
    obj.p_message1 = order_message;
    this.setPreviousData(obj);
    this.setDataFile(this.PREVIOUS_DATANAME,JSON.stringify(obj, null, 2));
  }
  // API usage method
  getTicker() {
    var json = UrlFetchApp.fetch(
      this.DEF_BASEURL + this.DEF_VERSION + this.DEF_GETTICKER + this.BITFLYER_OPTION.product_code);
    var jsonData = JSON.parse(json);
    return parseFloat(jsonData.ltp);
  }
  sendChildOrder() {
    var url = this.DEF_BASEURL + this.DEF_VERSION + this.DEF_SENDORDER,
        para = Object.assign({}, this.BITFLYER_OPTION ),
        timestamp = Date.now().toString(),
        method = 'POST',
        path = this.DEF_VERSION + this.DEF_SENDORDER,
        body = JSON.stringify( para ),
        text = timestamp + method + path + body,
        signature = Utilities.computeHmacSha256Signature(text, this.BITFLYER_API_SECRET);
    var sign = signature.reduce(function(str,chr) {
        chr = (chr < 0 ? chr + 256 : chr).toString(16);
        return str + (chr.length==1?'0':'') + chr;
      },'');
    var headers = {
      'ACCESS-KEY'        : this.BITFLYER_API_KEY,
      'ACCESS-TIMESTAMP'  : timestamp,
      'ACCESS-SIGN'       : sign,
      'Content-Type'      : 'application/json'
    };
    var options = {
      method  : method,
      headers : headers,
      payload : body,
      muteHttpExceptions: true
    };
    var order_ticker = this.getTicker();
    var response = UrlFetchApp.fetch(url, options);
    if( response != null ){
      var json = response.getContentText();
      var jsonData = JSON.parse(json);
      if( jsonData.child_order_acceptance_id ) {
        this.BITFLYER_ISORDER = true;
        para.datetime = Utilities.formatDate( new Date(), 'Asia/Tokyo', 'yyyy/MM/dd hh:mm:ss');
        para.child_order_acceptance_id = jsonData.child_order_acceptance_id;
        para.ticker = order_ticker;
      } else {
        para.error = json;
      }
    } else {
      para.error = "Connection Error";
    }
    return para;
  }
  // DriveApp usage method
  setDataFile(filename, contents){
    const scriptid = ScriptApp.getScriptId();
    const parentFolder = DriveApp.getFileById(scriptid).getParents();
    const folderid = parentFolder.next().getId();
    const folder = DriveApp.getFolderById(folderid);
    const file = this.getDataByName(folder, filename);
    if( file ){
      file.setContent(contents);
    } else {
      const blob = Utilities.newBlob('', 'text/plain', filename).setDataFromString(contents, 'utf-8');
      folder.createFile(blob);
    }
  }
  getDataFile(filename){
    const scriptid = ScriptApp.getScriptId();
    const parentFolder = DriveApp.getFileById(scriptid).getParents();
    const folderid = parentFolder.next().getId();
    const folder = DriveApp.getFolderById(folderid);
    const file = this.getDataByName(folder, filename);
    if( file ){
      const filedata = file.getBlob().getDataAsString("utf-8"); 
      return filedata ? filedata : null;
    } else {
      return null;
    }
  }
  getDataByName (folder, filename) {
    var files = folder.getFilesByName(filename)
    while (files.hasNext()) {
      return files.next();
    }
    return null;
  }
}
※GitHubでも公開してます:https://github.com/sakurateam-info/gasbitcoin

新規作成時にコーディングされた「myFunction」の下に貼り付けます。

(2) myFunction のスクリプトをコピー

ビットコインを自動売買するための条件等をスクリプトに記載します。スクリプトは「myFunction」(任意の名前でも大丈夫)に記載し、この中で「bitFlyer用スクリプト」を利用します。

次のコードは「myFunction」に記載するサンプルコードです。このまま利用できます。
この例は、特に条件なく0.001 BTCの売却・購入を繰り返すだけのコードです。

function myFunction() {
  const bit = new bitFlyer();
  bit.BITFLYER_API_KEY = 'APIキー';
  bit.BITFLYER_API_SECRET = 'APIシークレット';
  
  var data = bit.getData();       // 履歴データ取得
  var ticker = bit.getTicker();   // 現在価格を取得(条件等で使用する場合)

  if( data.side === "SELL" ) {
    bit.setSendOrder('BUY');      // 購入(既定値は0.001 BTC)
  } else {
    bit.setSendOrder('SELL');     // 売却(既定値は0.001 BTC)
  }

  bit.Log();                      // 結果表示
}

(3) スクリプトにAPIキーを設定

記載したコードに「API Key」と「API Secret」を設定します。bitFlyerのAPIを有効化した際に表示された「API Key」と「API Secret」をコピーして貼り付けます。

スクリプトが完成したらスクリプトを保存します。

3.ビットコインの売買

(1) スクリプトの実行

作成したスクリプトを実行します。実行する名前を確認して「実行」をクリックします。

初回実行時はGASの承認画面が表示されます。ログイン中のアカウントを選択します。

詳細から承認画面に移り「許可」をクリックします。

(2) 実行ログの確認

スクリプトを実行する画面下にログが表示されます。表示されない場合は、上部のメニューから「実行ログ」をクリックします。ログにエラーが表示されていないことを確認します。

初回実行時は、今回価格の個所に「BUY(購入)」と表示されます。

再度実行すると、今回価格の個所に「SELL(売却)」と表示されます。

初回、タイムアウトのエラーが発生する場合があります。何回か実行すると正常に実行できる場合があります。

(3) 売買状況の確認

bitFlyerにてビットコインの売買状況を確認します。スクリプトを実行した結果について、bitFlyerの「お取引レポート」で正しく売買されているかを確認します。スクリプトを実行した回数分、取引が行われていることが分かります。

(4) 取引データの確認

スクリプトを実行した結果のデータはGoogleドライブに保存されます。スクリプトを保存している場所と同じ場所に「data.txt」が作成されます。
この「data.txt」に実行記録が格納されるため、この内容を参照して前回との比較などの条件を指定することが可能です。

項目内容サンプル値
child_order_type成行注文MARKET
side買い注文・売り注文SELL
size注文数量(~0.001 BTC)0.001
ticker取引時価格(円)5,500,000 
message取引時コメント(任意)
prevside前回成約時注文BUY
prevsize前回成約時注文数量0.001
prevticker前回成約時価格(円)5,000,000
prevmessage前回成約時コメント
approxincome推定総損益(円)500
approxfee推定総手数料(円)5
tradecount成約回数2
p_side1~10
取引履歴(10回分)
SELL ,
BUY
p_size1~10
取引履歴(10回分)
0.001,
0.001
p_ticker1~10
取引履歴(10回分)
5,500,000
5,000,000
p_message1~10 
取引履歴(10回分)

data.txt

4.ビットコインの自動売買

(1) トリガー(自動実行)の設定

スクリプトを自動実行する設定を行います。GASのトリガー(自動実行)を使用することで、一定間隔や決まった時間帯にスクリプトを実行できます。(5分間隔、毎日12時、毎週日曜日、毎月1日など)

メニューから「トリガー」を選択し、「トリガーを追加」をクリックします。

実行名(myFunction)を確認し、時間を指定します。この例では5分に1回、スクリプトを実行します。「保存」をクリックすると、トリガーが開始されます。

(2) 自動売買状況の確認

トリガーの実行結果はメニューの「実行数」から確認ができます。指定した間隔でスクリプトが実行されているのが確認できます。

自動売買状況はbitFlyerの「お取引レポート」で確認ができます。指定した間隔で売買が行われているのがわかります。実際には注文から取引までにタイムラグがあるため、厳密な間隔での取引にはなりません。

(3) トリガー(自動実行)の削除

ビットコインの自動売買を停止する場合は、トリガー(自動実行)を削除します。
メニューの「トリガー」から、対象のトリガーを選択して「トリガーを削除」をクリックします。

5.スクリプトのアレンジ

ビットコインの取引量を変更して購入

ビットコインの取引量を変更する場合は、取引量を指定します。また、取引結果のメール通知を行う場合は、メールアドレスの指定を行います。

function myFunction() {
  const bit = new bitFlyer();
  bit.BITFLYER_API_KEY = 'APIキー';
  bit.BITFLYER_API_SECRET = 'APIシークレット';
  bit.BITFLYER_OPTION.size = 0.01;       // 0.01 BTCの指定
  bit.BITFLYER_MAIL = 'メールアドレス';   // 取引時にメール通知

  var data = bit.getData();
  var ticker = bit.getTicker();

  if( data.side === "SELL" ) {
    bit.setSendOrder('BUY');
  } else {
    bit.setSendOrder('SELL');
  }

  bit.Log();
}

前回の取引価格を判定して購入

前回の取引情報を参照する場合は、getDtataの内容を使用します。前回情報は「data.txt」の内容です。前回内容を利用することで、上昇トレンドや下降トレンドといった判定が可能です。

function myFunction() {
  const bit = new bitFlyer();
  bit.BITFLYER_API_KEY = 'APIキー';
  bit.BITFLYER_API_SECRET = 'APIシークレット';

  var data = bit.getData();       // 履歴データ取得
  var ticker = bit.getTicker();   // 現在価格を取得(条件等で使用する場合)

  // 前回と比較して、5,000円、上昇または下降があれば売買
  if( data.side === "SELL" ) {
    if( ticker < ( data.ticker - 5000 ) ) {
      bit.setSendOrder('BUY');
    } else {
      bit.setNoOrder();
    }
  } else {
    if( ticker > ( data.ticker + 5000 ) ) {
      bit.setSendOrder('SELL');
    } else {
      bit.setNoOrder();
    }
  }

  bit.Log();
}

bitFlyer用スクリプトの主なメソッドは以下のとおりです。

主なメソッド内容
.getData()取引履歴取得(JSON形式:data.txt)
※表示例
var data = bit.getData();
Logger.log( data.p_ticker1 );
.setSendOrder
(order_type ,order_message)
売買実行+記録
・order_type は”SELL” or “BUY”
・order_messageは任意
.setNoOrder
(order_message)
履歴の記録のみ
・order_messageは任意
.getTicker()現在価格の取得
.Log()ログの表示

6.まとめ

ビットコイン取引所のAPIとGoogle Apps Script(GAS) 利用するとお手軽にビットコインの自動売買ができます。bitFlyer用スクリプトを利用することで、簡単に自己流の条件で自動売買を設定することができます。

タイトルとURLをコピーしました