断クリプト日記(n日ぶりm回目)

暗号通貨ガチホしつつbotトレード極めます

ビットコインのトレードbotを書いてみた

どうもみなさん、ガチホしていますか?

最近クリプト界隈で話題となっているビットコインのトレードbotを書いてみましたので、
備忘録として書き残しておきます。

botの作り方などはQiitaや有料noteなんかにもいくつか投稿がありますが、
テーマ別となっており局所的にソースコードが載っていたりと、全体像が分かりにくく、
プログラムを少しかじったことがある人でもモチベーションを維持しながら
最後まで作りきることが難しいのでは感じていました。

そこで、私の尊敬するbotトレーダーAKAGAMIさんとは真逆の路線で、
読者の皆様に"釣り竿"ではなく、"魚"そのものを与え、
まずはbotトレードに興味を持っていただこうというスタンスで記事の公開を始めました。

AKAGAMIさんがいなければ自分自身ここまで来ることはできなかったので、敬意を表します。

目次

1.本記事の対象読者

  • プログラミングの基礎知識がある(言語問わず、簡単な機能を実装できる)
  • pythonの環境構築ができる(anacondaやpipが分かる、eclipseなどのIDEが使える)
  • bitFlyerに口座を開設している

2.概要と技術テーマ

概要

1分ごとにRSIを取得し、設定した基準を割った場合(売られすぎor買われすぎ)にエントリーし、
RSIがニュートラルになったらポジションをクローズする平均回帰型のスキャルbotです。
公開したソースコードbitFlyerのBTC-FXで取引するための実装にしてあります。

技術テーマ
  • ccxt
     →暗号通貨取引所のAPIをall-in-oneで使えるライブラリ
  • TA-Lib
     →インディケーターの算出を自動でできるライブラリ
      pythonで利用するには、ラッパーのインストールが必要。
  • CryptowatchAPI
     →bitFlyerの価格を取得するために利用しています



3.パッケージの構成とソースコード

パッケージの構成

f:id:cryptocurrency_chudoku:20180321155157p:plain



TradingController.py (メインクラス)
import ccxt
import time
from config import Apiconfig    as  api
from config import Tradeconfig  as  tra
from ExecLogic import EXEC_LOGIC
from ExecTicker import EXEC_TICKER


# ロード処理

# 取引ペア
symbol=tra.symbol

# APIキー
bitflyer = ccxt.bitflyer({
'apiKey': api.bf_api_key,
'secret': api.bf_api_secret,
})

# 実行クラス
ticker = EXEC_TICKER()
logic  = EXEC_LOGIC()


while 1==1:
    print('sleep')
    time.sleep(tra.crawling_time)

    # 初期ポジション
    position=None

    try:
        # 終値の取得
        close_price=ticker.get_ticker()
        # ロジッククラスの判定
        position_flg=logic.exec_rsi(close_price)
        print(position_flg)
    except:
        print('価格の取得に失敗しました。APIエラーのため再実行します。')


    # ノーポジションからエントリー
    if position_flg == 1 or position_flg == -1 :

        try:
            # 注文指定
            if   position_flg ==  1: position='buy'
            elif position_flg == -1: position='sell'
            # 発注
            order = bitflyer.create_order(symbol, type='market',side=position, amount=tra.lot)
            # 注文id
            position=order['id']
            print('ポジションを取ります')
            print(order)
        except:
            # APIエラー
            print('ポジションの取得に失敗しました。APIエラーのため再実行します')
            position=None

    while position:
        print('sleep')
        time.sleep(tra.crawling_time)

        try:
            # 終値の取得
            close_price=ticker.get_ticker()
            # ロジッククラスの判定
            exit_position_flg=logic.exec_exit_rsi(close_price,position_flg)
            print(exit_position_flg)
        except:
            # APIエラー
            print('価格の取得に失敗しました。APIエラーのため再実行します')

        if exit_position_flg + position_flg == 0:

            try:
                # 注文指定
                if   exit_position_flg ==  1: position='buy'
                elif exit_position_flg == -1: position='sell'
                # 発注
                order = bitflyer.create_order(symbol, type='market', side=position, amount=tra.lot)
                print('ポジションをクローズします')
                print(order)
                position=None
                position_flg=0
                exit_position_flg=0
            except:
                # APIエラー
                print('ポジションのクローズに失敗しました。APIエラーのため再実行します')
        else :
            exit_position_flg=0



ExecLogic.py (売買ロジックの判定クラス)
import talib as ta



class EXEC_LOGIC:

    def exec_rsi(self,close_price):

        entryflg=0

        #ta-lib
        data=ta.RSI(close_price, timeperiod=14)

        print(data)
        RSI=data[120]
        print(RSI)

        if   RSI <= 35.0:
            print("成行買い")
            entryflg=1

        elif RSI >= 65.0:
            print("成行売り")
            entryflg=-1

        else:
            print("ノーポジション")

        return  entryflg


    def exec_exit_rsi(self,close_price,position_flg):

        exitflg=0

        #ta-lib
        data=ta.RSI(close_price, timeperiod=14)

        print(data)
        RSI=data[120]
        print(RSI)

        if   RSI <= 53.0 and position_flg == -1:
            print("手仕舞い_成行買い")
            exitflg=1

        elif RSI >= 47.0 and position_flg == 1:
            print("手仕舞い_成行売り")
            exitflg=-1

        else:
            print("HOLD")

        return  exitflg



ExecTicker.py (価格データ取得クラス)
import requests
import json
import datetime as dt
from datetime import timedelta as td
import numpy as np
from config import Tradeconfig as tra

class EXEC_TICKER:

    def get_ticker(self):


        # 現在時刻から指定した時間間隔の時刻を取得
        endDate = dt.datetime.now()
        startDate = endDate + td(hours=tra.span)

        # 時刻データのフォーマット変換
        startTimestamp = startDate.timestamp()
        endTimestamp = endDate.timestamp()

        # 価格データのAPIリクエスト@cryptowatch
        # 1分足を取得
        query = {"periods": "60", "after": str(int(startTimestamp)), "before": str(int(endTimestamp))}
        res = json.loads(requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc", params=query).text)["result"]["60"]
        res = np.array(res)

        #ta-libに渡す形式
        close_price = res[:, 4]

        return  close_price



Tradingconfig.py(トレードに関するパラメータファイル)
# 取引ペア
symbol='FX_BTC_JPY'

# データを取得する期間
span=-2 # 2hours

# ロット数(BTC)
lot=0.01

# クローリングTime
crawling_time=60    # 60sec



Apiconfig.py(アクセス情報をハードコーディングしたファイル)
# APIのアクセスキーをここに書く
bf_api_key=

# APIのシークレットキーをここに書く
bf_api_secret=

4.考察

バックテストをせずに即本番運用しましたが、パフォーマンスとしては"トントン"でした。
①→➁や①'→➁'のようなレンジ相場ではRSIと価格がほぼ連動してくれているので問題ないですが、
➂→④のようなトレンドが継続する場合になるとエントリーポイントからずるずると含み損になります。

f:id:cryptocurrency_chudoku:20180323091632p:plain
※ちなみに➂→④の時は、再び➂と④の間でRSIのラインを割ったときに
botが持っているポジションよりも多く、私が直接ナンピンしてエントリーして、
RSIがニュートラルに戻った時に利確します。

追加機能として上記のナンピンエントリーが実装できれば、
勝率がかなり高めのbotになるのではないかと考えています。

5.備考

Win10の64bit環境でTA-Libのラッパーをインストールするまでに
C#コンパイルツールがデフォルトで入っていなかったり、
64bit専用のVisual Studioコマンドプロンプトコンパイルしないといけないなど
地雷除去に2日かかりましたorz
※Gitの説明を見ればなんとか解決できますが、そもそもlinuxmac環境であれば瞬殺です。



本記事を最後まで読んでいただきありがとうございました。
botトレードをこれから始めたい人の何かきっかけになれたら幸いです。