こんにちは、はやたす(@hayatasuuu )です。
今回の講義では、途中まで作成していた自動売買ロジックを、完成させていきたいと思います。
前回の記事はこちら

とはいえ自動売買ロジックは、ほぼ完成しています。
あとは少しだけクラスを編集しつつ、売買ロジックを書いていくだけです。
今回の内容まで取り組めば、最小限のコード作成は完了します。
残りもわずかなので、ぜひ頑張っていきましょう!
import configparser
import time
import pandas as pd
from coincheck import Coincheck
conf = configparser.ConfigParser()
conf.read('config.ini')
ACCEES_KEY = conf['coincheck']['access_key']
SECRET_KEY = conf['coincheck']['secret_key']
coincheck = Coincheck(access_key=ACCEES_KEY, secret_key=SECRET_KEY)
interval = 1
duration = 20
df = pd.DataFrame()
while True:
time.sleep(interval)
df = df.append(
{'price': coincheck.last}, ignore_index=True
)
if len(df) < duration:
continue
df['SMA'] = df['price'].rolling(window=duration).mean()
df['std'] = df['price'].rolling(window=duration).std()
df['-2σ'] = df['SMA'] - 2*df['std']
df['+2σ'] = df['SMA'] + 2*df['std']
# もし-2σを割ったら
if df['price'].iloc[-1] < df['-2σ'].iloc[-1]:
print('buy!!!')
# もし+2σを割ったら
if df['+2σ'].iloc[-1] < df['price'].iloc[-1]:
print('sell!!!')
Contents
条件分岐に応じてビットコインを自動売買しよう!
自動売買プログラムを完成させるにあたり、今回は以下の手順で紹介していきます。
- STEP① : Coincheckクラスの編集
- STEP② : 購入ロジックの作成
- STEP③ : 売却ロジックの作成
- STEP④ : コードの微調整
それでは順番に見ていきましょう!
STEP① : Coincheckクラスの編集
まずはCoincheckクラスを編集していきます。
いまcoincheck.py
の中身は、以下のようになっていました。
import hmac
import hashlib
import json
import time
import requests
class Coincheck(object):
def __init__(self, access_key, secret_key):
self.access_key = access_key
self.secret_key = secret_key
self.url = 'https://coincheck.com'
def _request(self, endpoint, params=None, method='GET'):
nonce = str(int(time.time()))
body = json.dumps(params) if params else ''
message = nonce + endpoint + body
signature = hmac.new(self.secret_key.encode(),
message.encode(),
hashlib.sha256).hexdigest()
headers = {
'ACCESS-KEY': self.access_key,
'ACCESS-NONCE': nonce,
'ACCESS-SIGNATURE': signature,
'Content-Type': 'application/json'
}
if method == 'GET':
r = requests.get(endpoint, headers=headers, params=params)
else:
r = requests.post(endpoint, headers=headers, data=body)
return r.json()
def ticker(self):
endpoint = self.url + '/api/ticker'
return self._request(endpoint=endpoint)
@property
def last(self):
return self.ticker()['last']
def trades(self, params):
endpoint = self.url + '/api/trades'
return self._request(endpoint=endpoint, params=params)
def order_books(self, params=None):
endpoint = self.url + '/api/order_books'
return self._request(endpoint=endpoint, params=params)
def balance(self):
endpoint = self.url + '/api/accounts/balance'
return self._request(endpoint=endpoint)
@property
def position(self):
balance = self.balance()
return {k: v for k, v in balance.items()
if isinstance(v, str) and float(v)}
def order(self, params):
endpoint = self.url + '/api/exchange/orders'
return self._request(endpoint=endpoint, params=params, method='POST')
このCoincheck
クラスに対して、メソッドを2つとプロパティを1つ追加していきます。
具体的には、以下の3つです。
- メソッド
transaction
: 自分の最近の取引内容を確認する - プロパティ
ask_rate
: 自分が購入したビットコイン価格を取得する - メソッド
rate
: ビットコインの枚数に応じた価格を確認する
それぞれの使い方は、後ほど売買プログラムを組むときに紹介していきます。
とりあえず今は、これらのメソッドとプロパティを追加していきましょう。
とはいえ、メソッドの追加に関しては、第12回目と同じ要領で作成できます。
各メソッドのアクセス先をCoincheckAPIドキュメントで確認すると、以下のようになっています。
- メソッド
transaction
: /api/exchange/orders/transactions - メソッド
rate
: /api/exchange/orders/rate
あとはパラメータ付与などを気をつければ、以下のようにコードを作成できますね!
"""
中略
"""
class Coincheck(object):
def __init__(self, access_key, secret_key):
self.access_key = access_key
self.secret_key = secret_key
self.url = 'https://coincheck.com'
"""
中略
"""
def order(self, params):
endpoint = self.url + '/api/exchange/orders'
return self._request(endpoint=endpoint, params=params, method='POST')
def transaction(self):
endpoint = self.url + '/api/exchange/orders/transactions'
return self._request(endpoint=endpoint)
def rate(self, params):
endpoint = self.url + '/api/exchange/orders/rate'
return self._request(endpoint=endpoint, params=params)
transaction
メソッドは、単純にトレード履歴を取得するだけなので、パラメータが必要ありません。
反対にrate
メソッドの場合は、通貨ペアとその枚数が必要になるので、パラメータを設置します。
これでメソッドの追加が完了しました。
いったんtransaction
メソッドを使ってみたいと思います。
皆さんは実行しなくても大丈夫ですが、もし手元でも確認するなら対話シェルを開いて、以下のコードを書きましょう。
>>> import configparser
>>> from coincheck import Coincheck
>>> conf = configparser.ConfigParser()
>>> conf.read('config.ini')
['config.ini']
>>> ACCEES_KEY = conf['coincheck']['access_key']
>>> SECRET_KEY = conf['coincheck']['secret_key']
>>> coincheck = Coincheck(access_key=ACCEES_KEY, secret_key=SECRET_KEY)
>>> coincheck.transaction()
そうすると、取引履歴を取得できるはずです。
ただ、ここで取得できる取引履歴は、かなり膨大な情報になっていますよね。
今回、取引履歴を使う理由は、直近のビットコイン購入価格を把握するためです。
ゆえに、これらすべての情報は必要ありません。
そこで、ask_rate
プロパティを作成することで、直近のビットコイン購入価格だけ取得できるようにします。
transaction
メソッドの取得結果を確認しつつ、直近の購入価格だけ取得するには、以下のようにコードを作成すれば良さそうですね!
class Coincheck(object):
"""
中略
"""
def transaction(self):
endpoint = self.url + '/api/exchange/orders/transactions'
return self._request(endpoint=endpoint)
@property
def ask_rate(self):
transaction = self.transaction()
ask_transaction = [d for d in transaction['transactions']
if d['side'] == 'buy']
return float(ask_transaction[0]['rate'])
def rate(self, params):
endpoint = self.url + '/api/exchange/orders/rate'
return self._request(endpoint=endpoint, params=params)
これで、直近のビットコイン購入価格だけ取得できるはずです。
対話シェルの続きで、ask_rate
プロパティの出力を確認してみたいと思います。
>>> coincheck.ask_rate
6553604.0
出力を確認すると、ビットコインの価格らしき値を取得できました。
これが直近の購入価格なのか、TraderViewを使って確認してみましょう。

しっかり直近の価格を取得できていますね!
これでメソッドとプロパティの作成は大丈夫だと思います。
STEP② : 購入ロジックの作成
そうしたらmain.py
を編集して、購入ロジックを完成させていきましょう。
現状だと、最新価格の取得とボリンジャーバンドの計算まで、コードを作成してあったはずです。
"""
中略
"""
interval = 1
duration = 20
df = pd.DataFrame()
while True:
time.sleep(interval)
df = df.append(
{'price': coincheck.last}, ignore_index=True
)
if len(df) < duration:
continue
df['SMA'] = df['price'].rolling(window=duration).mean()
df['std'] = df['price'].rolling(window=duration).std()
df['-2σ'] = df['SMA'] - 2*df['std']
df['+2σ'] = df['SMA'] + 2*df['std']
# もし-2σを割ったら
if df['price'].iloc[-1] < df['-2σ'].iloc[-1]:
print('buy!!!')
# もし+2σを割ったら
if df['+2σ'].iloc[-1] < df['price'].iloc[-1]:
print('sell!!!')
ここでは、そのあとの購入ロジックを完成させます。
とは言っても、あとは条件分岐の中で、クラス内に定義してあったメソッドを使っていくだけです。
ビットコインの購入には、coincheck.order(params)
を使いました。
このときparams
の中には、以下3つの引数が必要になります。
pair
: 通貨ペア(今回はbtc_jpy
)order_type
: 取引方法(今は購入なのでmarket_buy
)market_buy_amount
: 購入で使用する日本円
pair
とorder_type
は問題ないですよね。
通貨ペアはBTC/JPYですし、取引方法は成行の買いです。
ここで問題になるのは、購入で使用する日本円です。
購入したいビットコインの量は0.005BTCと決まっているけど、日本円の金額は\(-2\sigma\)を下回ったときなので、毎回同じとは限りません。
つまり、購入したいビットコインのレートに応じた、日本円の金額が分かっている必要があります。
この計算を行うには、先ほどCoincheck
クラス内に追加しておいたrate
メソッドを使います。
具体的には、購入したいビットコインの数量と、通貨ペア・取引方法をパラメータで渡します。
実際に対話シェルで試してみたのが以下です。
>>> coincheck.rate({'order_type': 'buy', 'pair': 'btc_jpy', 'amount': 0.005})
{'success': True, 'rate': '6559853.0', 'amount': '0.005', 'price': '32799.265'}
このようにcoincheck.rate()
で取引したいビットコインの数量を渡すと、日本円換算したprice
を取得できます。
これらを踏まえると、ビットコイン購入側のロジックは、以下のようになります。
coincheck = Coincheck(access_key=ACCEES_KEY, secret_key=SECRET_KEY)
interval = 1
duration = 20
AMOUNT = 0.005 # 追加
df = pd.DataFrame()
while True:
time.sleep(interval)
df = df.append(
{'price': coincheck.last}, ignore_index=True
)
"""
中略
"""
# もし-2σを割ったら
if df['price'].iloc[-1] < df['-2σ'].iloc[-1]:
market_buy_amount = coincheck.rate({'order_type': 'buy',
'pair': 'btc_jpy',
'amount': AMOUNT})
print('使用する日本円', market_buy_amount['price'])
params = {
'pair': 'btc_jpy',
'order_type': 'market_buy',
'market_buy_amount': market_buy_amount['price']
}
r = coincheck.order(params)
print('entry', r)
取引する数量は、あらかじめAMOUNT
で設定しておきました。
これで、ビットコインの購入ロジックは作成完了です。
STEP③ : 売却ロジックの作成
次に売却ロジックです。
いま作成してある条件分岐は「ビットコイン価格が\(+2\sigma\)より上だったら」になっています。
今回作成するロジックでは、これだけでなく、購入した価格を上振れたとき売却するようにしましょう。
つまり、作成するコードとしては以下のようになります。
"""
中略
"""
while True:
time.sleep(interval)
positions = coincheck.position # 追加
"""
中略
"""
# もし+2σを割ったら
if df['+2σ'].iloc[-1] < df['price'].iloc[-1] \
and coincheck.ask_rate < df['price'].iloc[-1]:
params = {
'pair': 'btc_jpy',
'order_type': 'market_sell',
'amount': positions['btc']
}
r = coincheck.order(params)
print('exit', r)
これで売却側のロジックも完成しました。
\(+2\sigma\)を超えて、かつ購入レートより高い位置にいる場合だけ、売却するようにしています。
STEP④ : コードの微調整
これで自動売買ロジックも完成…と言いたいところですが、もう少しだけやるべきことがあります。
具体的にいうなら、以下の3つです。
- 日本円の残高確認
- 売買ロジックの条件追加
- _request()メソッドの修正
文章だけ見てもよく分からないので、順番にコードを書きながら修正していきましょう。
日本円の残高確認
ビットコインの自動売買を行うには、口座内に入金しておく必要があります。
言い方を換えると、残高が入っていないときは、価格の取得を開始しないようにしたいです。
そのためには、価格情報をDataFrameに追加し始める前に、残高確認の判定をおこなっていきましょう。
つまり、ポジションの確認後に、以下のようなコードを書いていきます。
while True:
time.sleep(interval)
positions = coincheck.position
# 以下を追加する
if not positions.get('jpy'):
raise
df = df.append(
{'price': coincheck.last}, ignore_index=True
)
"""
中略
"""
これで残高が入っていない場合に、エラーを発生させるような処理を書けました。
売買ロジックの条件追加
いま書いてある売買ロジックだと、ポジションを持っていなくても、\(+2\sigma\)を上振れたらビットコインを売却するようになっています。
ただ、Coincheckでは現物取引しかできないので、ポジションがない状態で売りをかけるとエラーになります。
そこで、売買をおこなうときに、ポジションの有無も合わせて確認するようにしましょう。
そのためには、以下のように条件判定を変更してあげます。
while True:
"""
中略
"""
df['-2σ'] = df['SMA'] - 2*df['std']
df['+2σ'] = df['SMA'] + 2*df['std']
if 'btc' in positions.keys():
if df['+2σ'].iloc[-1] < df['price'].iloc[-1] \
and coincheck.ask_rate < df['price'].iloc[-1]:
params = {
'pair': 'btc_jpy',
'order_type': 'market_sell',
'amount': positions['btc']
}
r = coincheck.order(params)
print('exit', r)
else:
if df['price'].iloc[-1] < df['-2σ'].iloc[-1]:
market_buy_amount = coincheck.rate({'order_type': 'buy',
'pair': 'btc_jpy',
'amount': AMOUNT})
print('使用する日本円', market_buy_amount['price'])
params = {
'pair': 'btc_jpy',
'order_type': 'market_buy',
'market_buy_amount': market_buy_amount['price']
}
r = coincheck.order(params)
print('entry', r)
このように、先にポジションの有無を確認してから、売買を判定するようにコードを書き直しました。
売買ロジックの追加は、これで大丈夫かと思います。
_request()メソッドの修正
最後に、クラス内で使っていた_request()
メソッドを少し修正しましょう。
具体的には_request()
の先頭で、time.spleep(1)
の一文だけ追加してあげます。
class Coincheck(object):
def _request(self, endpoint, params=None, method='GET'):
time.sleep(1) # 追加
nonce = str(int(time.time()))
body = json.dumps(params) if params else ''
message = nonce + endpoint + body
signature = hmac.new(self.secret_key.encode(),
message.encode(),
hashlib.sha256).hexdigest()
headers = {
'ACCESS-KEY': self.access_key,
'ACCESS-NONCE': nonce,
'ACCESS-SIGNATURE': signature,
'Content-Type': 'application/json'
}
if method == 'GET':
r = requests.get(endpoint, headers=headers, params=params)
else:
r = requests.post(endpoint, headers=headers, data=body)
return r.json()
おそらく、先ほどまで書いていたmain.py
を実行すると、リクエスト同士の間隔が狭すぎてエラーになるはずです。
そのため、このように_request()
の内部で1秒だけ待機することで、余計なエラーを減らしました。
ここまで書ければ、今回使っていきたい自動売買ロジックの骨組みは作成完了です。
まとめ : 条件分岐に応じてビットコインを自動売買しよう!
というわけで、条件分岐の追加もできたので、自動売買ロジックも完成しました。
おそらく自動売買の作成は「思っていたより難しくない」と感じますよね。
確かに、できるだけ難しいコードにしないよう心がけていますが、それでもやるべきことはシンプルです。
これで、あとは作成したプログラムを実行するだけで、自動売買の取引を開始できます。
ただ、自動売買する上で、もう少し追加したい部分があります。
具体的には、売買結果がリアルタイムで分かると良いので、LINEに通知したいと思います。
また、いま作成したコードを自分のPCで動かし続けるのは、あまり良い判断とは言えません。
なので次回以降は、もう少しだけコードの追加をおこない、さらに自動売買プログラムを24時間365日動かすための設定をおこなっていきます。
もしLINEへの通知に興味がなかったり、自分のPCでプログラムを動かし続けるのであれば、ここまでの講義で十分です。
でも、もっと自動売買を進化させたいなら、次回以降の内容も間違いなく重要なので、続けて学習することをおすすめします。
ということで、次回も学習する場合は、もう少しだけお付き合い頂けると嬉しいです!
第15回目の記事はこちら

トップページはこちら
