Pythonで仮想通貨の移動平均線とボリンジャーバンド取得② 公開API動作プログラム

bitbank.ccの公開APIを使った、動作確認プログラムとなります。
プライベートAPIは動作しませんので、bitbank.ccに口座がなくても動作するプログラムとなります。

ベースにしたのは、以下のサイトのプログラムとなります。

参考 Python × ZaifAPI 取引時間をハックするプログラム【ボリンジャーバンド編】//サイト:テクノロジーであそぼ!

ベースとしたプログラムと大きな相違点

  • 平均、標準偏差の計算は、munpyライブラリを使っています。
  • 平均、標準偏差には、現時点(カレント)の市場価格は含まれません。過去のみです。
  • プライベートAPIは動かないようにしているため、売買は指定された価格、コイン数で必ず約定されるように動きます。このため、本当の売買とは動きが違うことを認識しておいてください。
  • プログラム開始時点の資産はプログラム内で指定しています。
  • モナコインの場合、ボリンジャーバンドの σ2 では、購入動作が動くタイミングが激減するので σ1 にしています。

標準偏差の計算について

ボリンジャーバンドでの標準偏差の計算式を、ネットで検索しても色々とあり、どれが正しいのかわかりません。
ベースにしたプログラムでは自前で計算しているのですが、numpyライブラリやExcelで計算させた値と相違があるので、ライブラリを使うようにしています。

import numpy as np

moving_avg = np.average(last_price[-PERIOD:])  # 平均
sigma = np.std(last_price[-PERIOD:], ddof=1)  # 標準偏差(予測)

ddof=1有りは、推測統計となり、ExcelのSTDEV.Sと同じになります。
ddof=1無しは、ExcelのSTDEV.P と同じになります。

前準備

スポンサードリンク

前記事のトレースのロガー、エラーのロガーを使います。

Pythonで仮想通貨の移動平均線とボリンジャーバンド取得① トレード用ロガー作成

特に、エラーロガーを使わないと、例外処理を入れている部分のエラー内容が判別出来ません。

slackにチャンネルを追加してWebhook用のURLを取得してください。

参考 Slack での着信 WebHook の利用Slackヘルプセンター

自分的には、Slackに通知するとBotを作っている感があって良いですよf^_^;)

URLが取得できれば、slackwebライブラリを使うので、WebhookのAPIを知る必要はありません。

slack通知が必要なければ以下を削除してください。

import slackweb

WEBHOOK_URL = API_data["slack_webhook_url"]
slack = slackweb.Slack(WEBHOOK_URL)

slack.notify(text=xxxxxxxx) # この部分は複数あります

key.jsonの作成

config/key.json

プライベートAPIを使わないのですが、bitbank_keyとbitbank_secretはプラグラム的に必要なので以下のように適当に指定してください。
slack_webhook_urlは、取得したURLを指定してください。

{
    "bitbank_key": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "bitbank_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "slack_webhook_url": "https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxxxx"
}

pythonにインストールが必要なライブラリ

以下でインストールしてください。Slackを使わない場合でもslackwebはインストールしてください。通信に必要なライブラリも一緒にインストールされるはずです。

pip install slackweb
pip install numpy

インストールに面倒だと思えたのは、bitbank.cc のライブラリです。

参考 python-bitbankccGitHub

Windowsでは、Gitをインストールしておく必要があります。
また、sudoはWindowsでは必要ありません。

pip install git+https://github.com/bitbankinc/python-bitbankcc.git

コード

注意
ここで書かれたコードを使うのは自己責任でお願いします

2018-02-10 仮想売買をした場合に、Slackへの資産通知をするように修正しました。また、コメントにしているプライベートAPIが間違っていたので修正しています。

# coding=utf-8

import json
import time
import pprint
import codecs
import slackweb
import numpy as np
import python_bitbankcc
from datetime import datetime
from decimal import (Decimal)
from logger import error_logger
from logger import trade_logger

with codecs.open('config/key.json', 'r', 'utf-8') as f:
    API_data = json.load(f)

WEBHOOK_URL = API_data["slack_webhook_url"]
slack = slackweb.Slack(WEBHOOK_URL)

API_KEY = API_data["bitbank_key"]
API_SECRET = API_data["bitbank_secret"]
COIN_NAME = 'mona'
COIN_PAIR = 'mona_jpy'

trade_log = trade_logger.TradeLogger('test', COIN_NAME)
error_log = error_logger.ErrorLogger('error')

PERIOD = 15  # ボリンジャーバンドの間隔数
LOOP_TIME = 15  # 市場価格取得間隔の秒数
CANCEL_LOOP_MAX = 4  # キャンセルするまでのループ回数 4x15=60秒
TRADE_HOLD_MAX = 4  # トレード直後に売買しないようにする回数 4x15=60秒

last_price = []  # 市場価格
cancel_flag = False  # キャンセル処理をするかのフラグ
cancel_loop_count = 0  # キャンセルまでのカウント
trade_hold_count = 0  # トレード後の売買処理しないカウント

low_borin = 0  # 低ボリンジャーバンド
moving_avg = 89999999  # 移動平均
sigma = 0  # 標準偏差
moving_avg_pre = 0  # 一つ前の移動平均
high_borin = 89999999  # 高ボリンジャーバンド

funds_jpy = 0  # 注文余力日本円
funds_coin = 0.0  # 注文余力コイン

last_trade_func = ''  # 取引機能
last_trade_order_id = 0  # 最後に取引したID
last_trade_size = 0.0  # 最後に取引したサイズ
last_trade_price = 0  # 最後に取引した価格
last_trade_price_pre = 0  # 一つ前の取引した価格(キャンセルした時用)
asset_info = False

message_func = ''  # トレードログ用
message_trade = ''  # トレードログ用
message_size = 0  # トレードログ用
message_price = 0.0  # トレードログ用

date_time = datetime.now()  # print用表示タイム
current_price = 0  # 一時的に保管する市場価格

#####################################
# テスト用                          #
user_coin_asset = 0.0  # 実売買しないのでテスト用コイン資産
user_jpy_asset = 5000.0  # 実売買しないのでテスト用日本円資産
#                                   #
#####################################


def check_results(trade_result):
    if (trade_result['status'] != 'FULLY_FILLED'):
        last_trade_order_id = trade_result['order_id']
        cancel_flag = True
        cancel_loop_count = 0
    else:
        print('■ 取引が完了しました。')
        # slack.notify(text='取引完了')
        last_trade_order_id = 0


if __name__ == '__main__':
    bitbank_public = python_bitbankcc.public()
    bitbank_private = python_bitbankcc.private(API_KEY, API_SECRET)

    last_trade_price = float(bitbank_public.get_ticker(COIN_PAIR)['last'])

    while True:
        message_func = ''
        message_trade = ''
        message_price = 0
        message_size = 0.0
        date_time = datetime.now()
        start_time = time.time()
        try:
            #result = bitbank_private.get_asset()
            #for currency in result['assets']:
            #    if currency['asset'] == 'jpy':
            #        funds_jpy = float(currency['free_amount'])
            #    if currency['asset'] == COIN_NAME:
            #        funds_coin = float(currency['free_amount'])
            #####################################
            # テスト用                          #
            funds_jpy = user_jpy_asset
            funds_coin = user_coin_asset
            #                                   #
            ##################################### 
            current_price = float(bitbank_public.get_ticker(COIN_PAIR)['last'])

        except:
            error_log.write()
            print("エラー:Cant get data")
            continue

        else:
            last_price.append(current_price)
            if len(last_price) <= PERIOD:
                print("■ 現在の情報です", len(last_price))
                print(str(date_time))
                print("市場取引価格:" + str(last_price[-1]))
                print(COIN_NAME + "資産:" + str(funds_coin))
                print("jpy資産:" + str(funds_jpy))
                print("最終取引価格:" + str(last_trade_price))

        if len(last_price) < 2 or asset_info == True:
            message_text = '現在の資産\n' + str(date_time) + '\njpy資産:' + str(funds_jpy) \
                           + '\n' + COIN_NAME + '資産:' + str(funds_coin)
            slack.notify(text=message_text)
            asset_info = False
        if len(last_price) > PERIOD:
            print('■資産、平均線、ボリンジャーバンド', len(last_price))
            print(str(date_time))
            print(COIN_NAME + '資産:',str(funds_coin))
            print('jpy資産:',str(funds_jpy))
            print('市場価格:',str(current_price))
            print('平均  :',str(moving_avg))
            print('高偏差:',str(high_borin))
            print('低偏差:',str(low_borin))
            message_func = '資産、平均、標準偏差'

        # コインを持っていて市場価格が以上なら利確、ただし、移動平均線が上昇傾向であれば保留
        if (funds_coin >= 0.001 and moving_avg < last_trade_price
                and trade_hold_count > TRADE_HOLD_MAX):
            if moving_avg > moving_avg_pre:
                print('上昇傾向なので保留')
            else:
                # 売却
                ask_amount = funds_coin
                try:
                    # trade_result = bitbank_private.order(COIN_PARE, last_price[-1], str(ask_amount), 'sell', 'limit')
                    #####################################
                    # テスト用                          #
                    time.sleep(1)
                    user_coin_asset -= float(ask_amount)
                    user_jpy_asset += (last_price[-1] * float(ask_amount))
                    trade_result = {'order_id': 0, 'status': 'FULLY_FILLED'}
                    #                                   #
                    #####################################
                    last_trade_func = 'ask'
                    last_trade_size = ask_amount
                    last_trade_price_pre = last_trade_price
                    last_trade_price = last_price[-1]
                    last_trade_order_id = trade_result['order_id']
                    trade_hold_count = 0
                    asset_info = True

                    print('■ 利確売却申請を行いました。')
                    pprint.pprint(trade_result)
                    print("売却注文価格:" + str(last_price[-1]))
                    print("売却注文量 :" + str(ask_amount))
                    message_func += '、利確'
                    message_trade = 'ask'
                    message_price = current_price
                    message_size = ask_amount

                    message_text = '■ 利確売却申請を行いました。\n' + str(date_time) \
                                   + '\nPrice:' + str(current_price) \
                                   + '\nSize:' + str(ask_amount)
                    slack.notify(text=message_text)

                except:
                    error_log.write()
                    print("エラー:cant trade[ask_up]")

                check_results(trade_result)

        # コインを持っていて低偏差より低い場合に損切り
        elif (funds_coin >= 0.001 and low_borin-5 > last_trade_price
              and trade_hold_count > TRADE_HOLD_MAX):
            ask_amount = funds_coin
            try:
                # 売却
                # trade_result = bitbank_private.order(COIN_PARE, last_price[-1], str(ask_amount), 'sell', 'limit')
                #####################################
                # テスト用                          #
                time.sleep(1)
                user_coin_asset -= float(ask_amount)
                user_jpy_asset += (last_price[-1] * float(ask_amount))
                trade_result = {'order_id': 0, 'status': 'FULLY_FILLED'}
                #                                   #
                #####################################
                last_trade_func = 'ask'
                last_trade_size = ask_amount
                last_trade_price_pre = last_trade_price
                last_trade_price = last_price[-1]
                last_trade_order_id = trade_result['order_id']
                trade_hold_count = 0
                asset_info = True

                print('■ 損切売却申請を行いました。')
                pprint.pprint(trade_result)
                print("売却注文価格:" + str(last_price[-1]))
                print("売却注文量 :" + str(ask_amount))
                message_func += '、損切'
                message_trade = 'ask'
                message_price = current_price
                message_size = ask_amount
                message_text = '■ 損切売却申請を行いました。\n' + str(date_time) \
                               + '\nPrice:' + str(current_price) + '\nSize:' + str(ask_amount)
                slack.notify(text=message_text)

            except:
                error_log.write()
                print("エラー:cant trade[ask]")

            check_results(trade_result)
        # コインを最小単位持っていない場合で、低偏差値より低い場合に購入
        elif (funds_coin < 0.001 and low_borin >= current_price
              and trade_hold_count > TRADE_HOLD_MAX):
            bid_amount = Decimal(funds_jpy * 0.9 / current_price).quantize(Decimal('0.001'))
            try:
                # trade_result = bitbank_private.order(COIN_PARE, last_price[-1], str(bid_amount), 'buy', 'limit')
                #####################################
                # テスト用                          #
                time.sleep(1)
                user_coin_asset += float(bid_amount)
                user_jpy_asset -= (last_price[-1] * float(bid_amount))
                trade_result = {'order_id': 0, 'status': 'FULLY_FILLED'}
                #                                   #
                #####################################
                last_trade_func = 'bid'
                last_trade_size = bid_amount
                last_trade_price_pre = last_trade_price
                last_trade_price = last_price[-1]
                last_trade_order_id = trade_result['order_id']
                trade_hold_count = 0
                asset_info = True

                print('■ 購入申請を行いました')
                pprint.pprint(trade_result)
                print("購入注文価格:" + str(last_price[-1]))
                print("購入注文量 :" + str(bid_amount))
                message_func += '、購入'
                message_trade = 'bid'
                message_price = current_price
                message_size = bid_amount
                message_text = '■ 購入申請を行いました。\n' + str(date_time) \
                               + '\nPrice:' + str(current_price) + '\nSize:' + str(bid_amount)
                slack.notify(text=message_text)

            except:
                error_log.write()
                print("エラー:cant trade[bid]")

            check_results(trade_result)

        if cancel_flag and cancel_loop_count > CANCEL_LOOP_MAX:
            trade_info = bitbank_private.get_order(COIN_NAME,last_trade_order_id)
            if trade_info["status"] == 'UNFILLED' or trade_info["status"] == 'PARTIALLY_FILLED':
                print("■ キャンセルしました")
                print(bitbank_private.cancel_order(COIN_NAME,last_trade_order_id))
                last_trade_price = last_trade_price_pre
                message_func += 'キャンセル'

            else:
                print("■ 取引が完了しました。")
            cancel_flag = False

        if len(last_price) >= PERIOD:
            if len(last_price) != PERIOD:
                trade_log.write(
                    func=message_func,
                    coin_asset=funds_coin,
                    jpy_asset=funds_jpy,
                    market_price=current_price,
                    order_id=last_trade_order_id,
                    trade=message_trade,
                    last_trade_price=last_trade_price,
                    price=message_price,
                    size=message_size,
                    mean_line=moving_avg,
                    sigma=sigma)

            cancel_loop_count += 1
            trade_hold_count += 1
            moving_avg_pre = moving_avg
            moving_avg = np.average(last_price[-PERIOD:])
            sigma = np.std(last_price[-PERIOD:], ddof=1)
            high_borin = moving_avg + sigma  # monaなので、σ1
            low_borin = moving_avg - sigma  # monaなので、σ1

        end_time = time.time()
        elpsed_time = end_time - start_time
        if elpsed_time > LOOP_TIME:
            elpsed_time %= LOOP_TIME
            print('ake time over', str(LOOP_TIME), 'sec')

        time.sleep(LOOP_TIME - elpsed_time)

以上、プログラムが動きますので、後は、売買のタイミングを色々と変更して利益が出るようにする必要があります。
このプログラムでの利益値に確定性は無いのですが、これで利益を出せないようだと本番には移行できませんねf^_^;)

Monappy: MBDQ39VHypMQwfyR8SshuHvfPNUz321F6B

Monacoinを投げる
モナゲ(tipmona)ってなに?
そもそもMonacoinってなに?

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください