見出し画像

bitflyer用のBOT雛形作ったよ!pythonのソースコード(各種発注関数付)

かぴぱら(@kapipara180)です。

今回はbitflyer用のBOT雛形を作成しました。色々なストラテジーを組むうちに、ストラテジーを入れ込む型が固まってきたので公開します!

BOT雛形の構成要素は以下の通りです。ひとつでも今実装していないもの、コーディングに詰まっているものがあれば有用だと思います。(一部は無料でソースを公開!)

2020/1/15:全部無料にしました!

・保有ポジションの取得関数

def POSITION(): # 保有ポジションの方向・数量を取得
                # LONGを持っている場合は建玉をプラスで返し、SHORTを持っている時はマイナスで返す
    try:
        ret = api.getpositions(product_code="FX_BTC_JPY")
    except Exception as e:
        logger.error("Error!")
        logger.error(e)
        return {'side': pos['side'], 'size': pos['size']}
    if len(ret) == 0:
        side = 'NO POSITION'
        size = 0
    elif ret[0]['side'] == 'BUY':
        side = 'LONG'
        sum = 0.0
        for o in ret:
            sum = sum + o['size']
        size = sum
    else:
        side = 'SHORT'
        sum = 0.0
        for o in ret:
            sum = sum + o['size']
        size = sum * -1.0 # SHORTはポジションをマイナスとみなす
    return {'side': side, 'size': size}

・残高取得関数

def BALANCE(): # 残高の取得 pnlは未実現損益 jpyは証拠金残高
    try:
        ret = api.getcollateral()
        pnl = ret['open_position_pnl']
        jpy = ret['collateral']
        return {'pnl': pnl, 'jpy': jpy}
    except Exception as e:
        logger.error("Error!")
        logger.error(e)
        return {'pnl': pnl, 'jpy': jpy}


・最大ロット取得関数

def GET_LOT(price_now):  # 建てられる最大ロットを取得
    Last_btc_value = price_now
    Free_jpy = BALANCE()['jpy']
    Free_levaraged = Free_jpy * leverage
    Capacity = Free_levaraged / Last_btc_value
    lot = float(Decimal(Capacity).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
    logger.info('ロットサイズ:%f '% lot)
    return lot

・成行注文関数
・ストップ注文関数

def STOP(side, price, size): # 逆指値注文を発注
    size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
    price = float(Decimal(price).quantize(Decimal('0'), rounding=ROUND_DOWN)) #priceは整数
    o = api.sendparentorder(order_method="SIMPLE", parameters=[{"product_code": "FX_BTC_JPY", "condition_type": "STOP", "side": side, "trigger_price": price,"size": size}])
    logger.info('新規STOP注文:%s %s %d %f' % (o,side,price,size))
    return price

・ストップリミット注文関数
・トレーリングストップ注文関数
・ATR計算関数(簡易版)
・クロスオーバー、クロスアンダー関数

def CROSSOVER(x,y): # xがyを上抜いたことを確認する(返値はbool)
    modori = x[-1] > y[-1] and x[-2] < y[-2]
    return modori

def CROSSUNDER(x,y): # xがyを下抜いたことを確認する(返値はbool)
    modori = x[-1] < y[-1] and x[-2] > y[-2]
    return modori

・ログの出力
・cryptowatchからの1分足の取得
・1つ前が陽線なら買いエントリー、1つ前が陰線なら売りエントリーをするセットアップ(雛形なのでこの上なく簡単なロジックにしました)
・決済ロジック(LONGで持っているときに売シグナルが出た際にドテン。とその逆)
・発注ロジック(ノーポジションの場合にシグナルが出たら発注)

以上の要素を整形し、コメントもふんだんに付けました。この雛形があれば、python初心者の方でもBOT作成にチャレンジできます!BOT作成初心者の方はぜひこの雛形から学び、最強のBOTを作成してください!

プロンプトの出力結果は ↓ のようになります。

画像1

以下ソース。解説はコメントを参照!それでもわからない場合はdiscordかノートにコメントをください!返せる範囲で迅速に返します。

#!/usr/bin/env python3
# coding: utf-8
# written by kapipara180 please follow me * - > - *
import pybitflyer
import time
import datetime
import logging
from decimal import (Decimal, ROUND_DOWN)
from pytz import timezone, utc
import requests

api_key = "xxxxxx" # ご自身のAPIキーを入力
api_secret = "xxxxxx" #ご自身の秘密鍵を入力

api = pybitflyer.API(api_key=api_key, api_secret=api_secret) # pybitflyer

leverage = 15 # レバレッジ
SLEEP = 30 # 処理間隔(s)

def CUSTOMTIME(*args): # 日本時間への変換
    utc_dt = utc.localize(datetime.datetime.utcnow())
    my_tz = timezone("Japan")
    converted = utc_dt.astimezone(my_tz)
    return converted.timetuple()

def POSITION(): # 保有ポジションの方向・数量を取得
                # LONGを持っている場合は建玉をプラスで返し、SHORTを持っている時はマイナスで返す
    try:
        ret = api.getpositions(product_code="FX_BTC_JPY")
    except Exception as e:
        logger.error("Error!")
        logger.error(e)
        return {'side': pos['side'], 'size': pos['size']}
    if len(ret) == 0:
        side = 'NO POSITION'
        size = 0
    elif ret[0]['side'] == 'BUY':
        side = 'LONG'
        sum = 0.0
        for o in ret:
            sum = sum + o['size']
        size = sum
    else:
        side = 'SHORT'
        sum = 0.0
        for o in ret:
            sum = sum + o['size']
        size = sum * -1.0 # SHORTはポジションをマイナスとみなす
    return {'side': side, 'size': size}

def BALANCE(): # 残高の取得 pnlは未実現損益 jpyは証拠金残高
    try:
        ret = api.getcollateral()
        pnl = ret['open_position_pnl']
        jpy = ret['collateral']
        return {'pnl': pnl, 'jpy': jpy}
    except Exception as e:
        logger.error("Error!")
        logger.error(e)
        return {'pnl': pnl, 'jpy': jpy}

def GET_LOT(price_now):  # 建てられる最大ロットを取得
    Last_btc_value = price_now
    Free_jpy = BALANCE()['jpy']
    Free_levaraged = Free_jpy * leverage
    Capacity = Free_levaraged / Last_btc_value
    lot = float(Decimal(Capacity).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
    logger.info('ロットサイズ:%f '% lot)
    return lot

def MARKET(side, size): # 成行注文を発注
    size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN)) #sizeは小数点二桁
    o = api.sendchildorder(product_code="FX_BTC_JPY", child_order_type="MARKET", side=side, size=size)
    logger.info('新規MARKET注文:%s %s %f' % (o, side, size))
    return size # 返りは特に使わないので適当

def STOP(side, price, size): # 逆指値注文を発注
    size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
    price = float(Decimal(price).quantize(Decimal('0'), rounding=ROUND_DOWN)) #priceは整数
    o = api.sendparentorder(order_method="SIMPLE", parameters=[{"product_code": "FX_BTC_JPY", "condition_type": "STOP", "side": side, "trigger_price": price,"size": size}])
    logger.info('新規STOP注文:%s %s %d %f' % (o,side,price,size))
    return price

def STOP_LIMIT(side, price, size): # ストップリミット注文を発注
    size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
    price = float(Decimal(price).quantize(Decimal('0'), rounding=ROUND_DOWN))
    o = api.sendparentorder(order_method="SIMPLE", parameters=[{"product_code": "FX_BTC_JPY", "condition_type": "STOP_LIMIT", "side": side, "price": price,"trigger_price":price,"size": size}])
    logger.info('新規STOP-LIMIT注文:%s %s %d %f' % (o,side,price,size))
    return price

def TRAIL(side, offset, size): #トレーリングストップ注文を発注
    size = float(Decimal(size).quantize(Decimal('0.01'), rounding=ROUND_DOWN))
    offset = float(Decimal(offset).quantize(Decimal('0'), rounding=ROUND_DOWN))
    o = api.sendparentorder(order_method="SIMPLE", parameters=[{"product_code": "FX_BTC_JPY", "condition_type": "TRAIL", "side": side, "size": size, "offset":offset}])
    logger.info('新規TRAIL注文:%s %s %d %f' % (o,side,offset,size))
    return offset

def ATR(high, low, length): # length分のatrを算出する ギャップは発生しないものとして簡略化
                            # high, lowは[0]が最も古く[-1]が最も新しい値が入っている配列を渡す
    atr_tmp = []
    for i in range(-length-1,-1): #直近の足の値は使わない
        atr_tmp.append(high[i]-low[i])
    atr = max(atr_tmp)
    return atr

def CROSSOVER(x,y): # xがyを上抜いたことを確認する(返値はbool)
    modori = x[-1] > y[-1] and x[-2] < y[-2]
    return modori

def CROSSUNDER(x,y): # xがyを下抜いたことを確認する(返値はbool)
    modori = x[-1] < y[-1] and x[-2] > y[-2]
    return modori

# ログ出力用
logger = logging.getLogger('LoggingTest')
logger.setLevel(10)
fh = logging.FileHandler('bitflyer_xxxx.log')  # ログファイル名
logger.addHandler(fh)
sh = logging.StreamHandler()
logger.addHandler(sh)
formatter = logging.Formatter('%(asctime)s %(lineno)03d: %(message)s', datefmt="%Y-%m-%d %H:%M:%S")
fh.setFormatter(formatter)
sh.setFormatter(formatter)
logging.Formatter.converter = CUSTOMTIME  # 日本時間を適用

pos = {'side': 'NO POSITION', 'size': 0}
bal = {'pnl': 0, 'jpy': 0}

logger.info('========== Basic strategy Start! ==========') # 繰り返しスタート
while True:
    try:

        # 直近の口座の状態を取得
        pos = POSITION()
        bal = BALANCE()
        logger.info('ポジション:%s %f' % (pos['side'], pos['size']))
        logger.info('未実現損益:%d , 証拠金残高:%d' % (bal['pnl'], bal['jpy']))

        #セットアップ・発注シグナルの記述***********************
        now = str(int(datetime.datetime.now().timestamp()))  # 現在時刻の取得
        buy_entry = False # 買い注文を入れるシグナルの初期化
        sell_entry = False # 売り注文を入れるシグナルの初期化

        # CryptowatchのAPIで1分足を取得 periodsは秒 from,toを指定しなければ直近500本が取得できる
        r = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?periods=60")
        ohlcv = r.json()['result']['60'] #1:o 2:h 3:l 4:c 5:v

        high = []
        low = []
        close = []
        open = []
        for i in range(len(ohlcv)): #使いやすいように分離
            open.append(ohlcv[i][1])
            high.append(ohlcv[i][2])
            low.append(ohlcv[i][3])
            close.append(ohlcv[i][4])

        # 非常に簡単な例として、一つ前の足が陽線ならば買いシグナル、陰線ならば売りシグナルとする
        # ここに売買ストラテジーを記述
        buy_entry = close[-2] > open[-2]
        sell_entry = close[-2] < open[-2]
        logger.info("買シグナル:%s 売りシグナル:%s" % (buy_entry, sell_entry))

        price_now = close[-1]  # 一番後ろが最新

        #********************************************************

        #注文ロジックの記述**************************************

        # ロットサイズの決定
        lot = GET_LOT(price_now)

        # 決済
        if pos["side"] == 'SHORT' and buy_entry: # SHORTの建玉を持っている際に買いシグナルが出たら決済
            entry = MARKET("BUY", abs(pos["size"])) # 成行買決済
            time.sleep(10) # 注文が通るまでタイムラグがあるので待つ
        if pos["side"] == 'LONG' and sell_entry: # LONGの建玉を持っている際に売りシグナルが出たら決済
            entry = MARKET("SELL", abs(pos["size"])) # 成行買決済
            time.sleep(10)

        # 発注
        if pos["side"] == 'NO POSITION' and buy_entry: # ポジションがないときに買いシグナルが出たら発注
            entry = MARKET("BUY", lot)
        if pos["side"] == 'NO POSITION' and sell_entry: # ポジションがないときに売りシグナルが出たら発注
            entry = MARKET("SELL", lot)

        # ********************************************************

        logger.info('終値:%d' % price_now)  # 最終価格
        logger.info('') # 1サイクル終了で空行を入れる
        time.sleep(SLEEP)  # SLEEPで指定した時間(s)だけ待って繰り返し

    except Exception as e: # 繰り返し処理の中でエラーが出た場合、エラーログを出力
        logger.error("Error!")
        logger.error(e)
        time.sleep(5)


ご意見・ご質問はnoteのコメントへどうぞ!






この記事が気に入ったらサポートをしてみませんか?