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 と同じになります。
前準備
前記事のトレースのロガー、エラーのロガーを使います。

特に、エラーロガーを使わないと、例外処理を入れている部分のエラー内容が判別出来ません。
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-bitbankccGitHubWindowsでは、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

モナゲ(tipmona)ってなに?
そもそもMonacoinってなに?
コメントを残す