背景
みなさん、最近仮想通貨が熱いですね。
BitcoinだけでなくEthereumやRipple等のアルトコインと呼ばれるものも
たくさん値上がりしています。
上記によるともう1000種類あるんですね。はえ~すごい。
が、しかし、国内の取引所を見てみるとどこも数が少なく(20種類弱くらい:2017/12時点)、
金の卵を見つけるには母数が少ないと感じてしまいました。
なので、
今回は有名な通貨もあり、アルトコインもたくさん(200種類以上ある)
Bittrexというサイトを使ってみました。
【使ってみた結果】
- 全部英語...(当たり前だ)
- ログインがとてもめんどくさい
- (ID/Pw -> ワンタイムパスワード -> メール認証 -> ID/Pw -> ワンタイムパスワード)
→心理的障壁が大きく、「買うのめんどくさい、売るのめんどくさい」で機会を逃すことがよくありました。
日頃から気軽に使ってるLINEで取引できたら、いいのになと思ったので、
今回「LINEで話せる美少女コンシェルジュ」を作るに至りました。
作ったもの
特定のメッセージを送ると、情報の照会や取引を行ってくれるLINE Botを作成しました。
美少女要素は画像だけです(*゚ー゚)
【実装した機能一覧】
- トレード履歴を取得
- 買い注文を出す
- 売り注文を出す
- 注文をキャンセルする
- 注文一覧を取得する
- 所有している通貨一覧を取得する
何がどう変わったか
価格をチェックしてから取引を行うまでの面倒な一連の処理がLINEのメッセージを送るだけでできるようになりました!!!
構成
- Messaging API (通称 LINE BOT) でメッセージを受け、
- 受けたメッセージをAWS API gatewayに転送し、
- AWS lambdaで処理を判断し、
- BittexのAPI経由で、仮想通貨取引・取引情報照会を行います。
- 受け取った結果をAWS lambdaで処理し、
- Messaging API(通称 LINE BOT)でメッセージをユーザ(スマフォ)に返します。
作り方
1. 環境設定
1-1. LINE Bot用アカウントを作成する
Line Bot用のアカウントを作成します。
- ページにアクセスし、キャプチャの手順でBotを作成していきます。
1-2. Bittrexでアカウントを作成する
(直接の入金は手間がかかるので、日本の取引所に入金し、送金することをおすすめします。)
->必要事項を入力し、アカウントを作成する
アカウントページへアクセスし、keyとsecretをコピーし、bittrex_keys.jsonへ貼り付ける
WITHDRAWは出金なので、今回は必要でない。
2. プログラミング
下記構成で作りました。
root/
├ [[pipしてきたライブラリたち]]
├ bittrex_utils/
│ ├bittrex_public.py --- bittrexの公開情報を扱うクラス
│ └bittrex_private.py --- bittrexの個人情報を扱うクラス
├ config/
│ ├bittrex_keys.json --- bittrexのAPIを扱うためのキー
│ └line_keys.json --- LINEのAPIを扱うためのキー
├ define/
│ ├common_define.py --- 汎用的な単語を管理
│ ├key_word_define.py --- チャットで使用するキーワードを管理
│ └message_define.py --- チャットで使用するメッセージを管理
├ line_utils/
│ └line_utils.py --- LINEのメッセージ送信を行うクラス
├ utils/
│ └reply.py --- bittrexの取引を処理し、メッセージを返すクラス
└ main.py --- 受け取ったメッセージを判別し、適切な処理へ振り分ける
※init.pyは省略
pipするパッケージはこちら
python-bittrex==0.2.2
line-bot-sdk
flask
【詳細はGithubをご覧ください】
https://github.com/speedkingg/Bittrex_LineBot
2-1.ソースコード
bittrex_utils/
Bittrex apiをラップして作ったクラス。
取引で使用するメソッドを作成し、まとめてある。
# -*- coding: utf-8 -*-
from bittrex import Bittrex # Bittrexで取引するためのAPI
class bittrex_public:
def __init__(self):
self.bittrex_public = Bittrex(None, None) # 公開情報を扱うBittrexオブジェクト
# 取引可能通貨サマリ一覧をList型で返す
def get_coin_summery_list(self):
coin_summery_list = []
response = self.bittrex_public.get_markets()
for item in response['result']:
coin_summery = str(item['MarketCurrencyLong'])
coin_summery_list.append(coin_summery)
return coin_summery_list
# 通貨の最小取引単位取得
def get_min_trade_size(self,market):
response = self.bittrex_public.get_markets()
for item in response['result']:
if item['MarketCurrency'] == market:
return item['MinTradeSize']
return False
# 通貨の終値取得
def get_last_price(self,market):
response = self.bittrex_public.get_marketsummary(market)
return response['result'][0]['Last']
# -*- coding: utf-8 -*-
import json # jsonを使用するため
from pprint import pprint # 表示用(jsonをきれいに表示してくれる)
from bittrex import Bittrex # Bittrexで取引するためのAPI
# 設定ファイル読み込み--------------
bittrex_keys_json = open('config/bittrex_keys.json', 'r')
bittrex_keys = json.load(bittrex_keys_json)
KEY = bittrex_keys["key"] # アクセスキー読み込み
SECRET = bittrex_keys["secret"] # シークレットキー読み込み
# -------------------------------
class bittrex_private:
def __init__(self):
# self.bittrex_public = Bittrex(None, None) # 公開情報を扱うBittrexオブジェクト
self.bittrex_private = Bittrex(KEY, SECRET) # 個人の情報を扱うBittrexオブジェクト
# トレード履歴を取得
def get_order_history(self):
response = self.bittrex_private.get_order_history()
pprint(response)
return response
# 買い注文を出す
# marketに通貨ペア、quantityに注文する量、rateに価格を指定
def buy_alt_coin(self, market, quantity, rate):
response = self.bittrex_private.buy_limit(market=market, quantity=quantity, rate=rate)
# 成功なら注文id、失敗ならfalseを返す
if response['success'] is False:
print(response)
return False
else:
return response['result']['uuid']
# 売り注文を出す
# marketに通貨ペア、quantityに注文する量、rateに価格を指定
def sell_alt_coin(self, market, quantity, rate):
response = self.bittrex_private.sell_limit(market=market, quantity=quantity, rate=rate)
# 成功なら注文id、失敗ならfalseを返す
if response['success'] is False:
print(response)
return False
else:
return response['result']['uuid']
# 注文をキャンセルする
def order_cancel(self, uuid):
response = self.bittrex_private.cancel(uuid=uuid)
# 成功ならtrue、失敗ならfalseを返す
print(response)
return response['success']
# 注文一覧を取得する
def get_orders(self):
order_list = []
response = self.bittrex_private.get_open_orders()
if response['success'] is False:
print(response)
return False
else:
# 注文が1件もない場合
if len(response['result']) == 0:
return None
for item in response['result']:
# 通貨の種類と量、注文IDを抜き出す
balance = {}
balance['market'] = item['Exchange']
balance['quantity'] = item['Quantity']
balance['uuid'] = item['OrderUuid']
order_list.append(balance)
return order_list
# 所有している通貨をList型で返す
def get_balances(self):
balance_list = []
response = self.bittrex_private.get_balances()
if response['success'] is False:
print(response)
return False
else:
for item in response['result']:
# 利用可能な通貨量が0の場合はスキップする
if item['Available'] == 0:
continue
# 通貨の種類と量を抜き出す
balance = {}
balance['currency'] = item['Currency']
balance['available'] = item['Available']
balance_list.append(balance)
return balance_list
config/
各種APIを利用するためのキーを保存するための設定ファイル
{
"key":"<Bittrex APIのアカウントページからコピーする>",
"secret":"<Bittrex APIのアカウントページからコピーする>"
}
{
"channel_access_token" : "<LINEの個人ページからコピーする>"
}
define/
返信用メッセージや処理分岐に使用するキーワードなど、直接処理内容に関係ない部分を抜き出して管理する。
# -*- coding: utf-8 -*-
class COMMON_DEFINE:
def __init__(self):
pass
# 通貨ペアをBTCにするための接頭語
PREFIX_BTC = "BTC-"
# 通貨の単位(BTC)
CURRENCY_UNIT_BTC = "btc"
# -*- coding: utf-8 -*-
class KeyWordDefine:
def __init__(self):
pass
# line_search_keyword
# 買い注文を出す
ORDER_BUY = ['買う', '買い', '買って']
# 売り注文を出す
ORDER_SELL = ['売る', '売り' , '売って']
# 注文をキャンセルする
ORDER_CANCEL = ['キャンセル']
# 注文一覧を取得する
ORDERS_LIST = ['注文一覧', 'オーダー一覧']
# 所有している通貨一覧を取得する
WALLET = ['所有', 'もってるやつ']
# ヘルプを出す
HELP = ['ヘルプ', 'へるぷ']
# -*- coding: utf-8 -*-
class MessageDefine:
def __init__(self):
pass
# line_reply_key_word
TRADE_ID = "取引ID"
# line_reply_message
# ヘルプメッセージ
HELP_MESSAGE = "メッセージフォーマットだよ\n\n"\
+ "[所有しているコイン一覧]\nkey_word: 所有、もってるやつ\n\n"\
+ "[買い注文]\nformat: 買い <通貨略称> <btc量>\n\n"\
+ "[売り注文]\nformat: 売り <通貨略称> <alt_coin量>\n\n" \
+ "[キャンセル]\nformat: キャンセル<注文ID>\n\n" \
+ "[注文一覧]\nkey_word: 注文一覧、オーダー一覧\n\n" \
+ "[ヘルプを出す]\nkey_word: へるぷ"
# 所有しているコイン一覧を返すときのメッセージ
OWNED_COIN_SUMMARY_MESSAGE = "今所有しているコインの一覧だよ"
# 処理に失敗した際に返すメッセージ
FAILED_TRADE_MESSAGE = "取引に失敗しちゃった。。。"
# コインの購入申請をしたときに返すメッセージ
APPLY_FOR_PURCHASE_MESSAGE = "指定したコインを購入申請したよ"
# 取引中の注文がないときに返すメッセージ
NO_ORDER_MESSAGE = "取引中の注文はないよ"
# 取引中の注文を返すメッセージ
ORDER_LIST_MESSAGE = "取引中の注文一覧だよ"
# 取引をキャンセルしたときに返すメッセージ
CANCEL_MESSAGE = "取引をキャンセルしたよ"
# 取引のキャンセルに失敗したときに返すメッセージ
FAILED_CANCEL_MESSAGE = "取引のキャンセルに失敗しちゃった。。。"
line_utils/
LINEで、メッセージを返信するときに使用するクラス。
LINE APIに合わせたフォーマットの整形やトークンの付与等を行う。
# -*- coding: utf-8 -*-
import json
from linebot.api import LineBotApi
from linebot.models import TextSendMessage
# 設定ファイル読み込み--------------
line_keys_json = open('config/line_keys.json', 'r')
line_keys = json.load(line_keys_json)
channel_access_token = line_keys["channel_access_token"] # シークレットキー読み込み
# -------------------------------
class line_utils:
def __init__(self):
# Line返信用オブジェクト作成
self.line_bot_api = LineBotApi(channel_access_token)
def line_reply(self,reply_token, reply_message):
self.line_bot_api.reply_message(reply_token, messages=TextSendMessage(reply_message))
utils/
main処理から呼び出される
Bitrtrexの取引メソッドとLINEのメッセージ返信メソッドを呼び出し、仮想通貨の取引とLINEの返信を行うクラス
# -*- coding: utf-8 -*-
from line_utils.line_utils import line_utils # lineでメッセージを送る
from bittrex_utils.bittrex_private import bittrex_private # bittrexの個人情報を扱う
from bittrex_utils.bittrex_public import bittrex_public # bittrexの公開情報を扱う
from define.common_define import COMMON_DEFINE
from define.message_define import MessageDefine
# lineメッセージを受け取って起動する
class reply():
def __init__(self):
self.line_utils = line_utils()
self.bittrex_private = bittrex_private()
self.bittrex_public = bittrex_public()
# フォーマットをメッセージにして返す
def match_keyword_help(self,reply_token):
# 定義からメッセージを読み込む
reply_message = MessageDefine.HELP_MESSAGE
# lineメッセージを返す
self.line_utils.line_reply(reply_token, reply_message=reply_message)
# 所有しているコイン一覧をメッセージにして返す
def match_keyword_wallet(self,reply_token):
# 定義からメッセージを読み込む
reply_message = MessageDefine.OWNED_COIN_SUMMARY_MESSAGE + "\n\n"
# 所有している通貨リストを取得する
balance_list = self.bittrex_private.get_balances()
# 所有しているコイン一覧を1行ずつメッセージに追記する
for item in balance_list:
reply_message += str(item['currency']) + ": " + str(item['available']) + "\n"
# lineメッセージを返す
self.line_utils.line_reply(reply_token,reply_message=reply_message)
# 指定したコインを買う
# line_message = "買い <通貨略称> <btc量>"
def match_keyword_buy(self,reply_token, line_message):
# 受け取ったlineメッセージを要素に分解する
line_message_list = line_message.split()
market_name = line_message_list[1]
btc_quantity = line_message_list[2]
# 通貨ペアをBTCにする
currency_pair = COMMON_DEFINE.PREFIX_BTC + market_name
# 最小取引量取得
min_trade_size = self.bittrex_public.get_min_trade_size(market_name)
# 終値取得
alt_last_price = self.bittrex_public.get_last_price(currency_pair)
#注文量計算
order_quantity = float(btc_quantity)/alt_last_price
# 最小取引量に合わせる
order_quantity = int(order_quantity/min_trade_size)*min_trade_size
# アルトコインを購入し、取引IDを受け取る
uuid = self.bittrex_private.buy_alt_coin(market=currency_pair ,
quantity=order_quantity,
rate=alt_last_price)
# メッセージ作成
# 取引失敗の場合
if uuid is False:
reply_message = MessageDefine.FAILED_TRADE_MESSAGE
# 取引成功でトレードが完了している場合
elif uuid == "":
reply_message = MessageDefine.APPLY_FOR_PURCHASE_MESSAGE + "\n\n" \
+ currency_pair + ": " + str(btc_quantity) + COMMON_DEFINE.CURRENCY_UNIT_BTC
# 取引成功でトレードが完了していない場合
else:
reply_message = MessageDefine.APPLY_FOR_PURCHASE_MESSAGE + "\n\n" \
+ currency_pair + ": " + str(btc_quantity) + COMMON_DEFINE.CURRENCY_UNIT_BTC\
+ "\n" + MessageDefine.TRADE_ID + " : " + uuid
# lineメッセージを返す
self.line_utils.line_reply(reply_token, reply_message=reply_message)
# 指定したコインを売る
# line_message = "売り <通貨略称> <alt_coin量>"
def match_keyword_sell(self,reply_token, line_message):
# 受け取ったlineメッセージを要素に分解する
line_message_list = line_message.split()
market_name = line_message_list[1]
alt_quantity = line_message_list[2]
# 通貨ペアをBTCにする
currency_pair = COMMON_DEFINE.PREFIX_BTC + market_name
# 最小取引量取得
min_trade_size = self.bittrex_public.get_min_trade_size(market_name)
# 終値取得
alt_last_price = self.bittrex_public.get_last_price(currency_pair)
# 最小取引量に合わせる
order_quantity = int(float(alt_quantity) / min_trade_size) * min_trade_size
# アルトコインを購入し、取引IDを受け取る
uuid = self.bittrex_private.sell_alt_coin(market=currency_pair ,
quantity=order_quantity,
rate=alt_last_price)
# メッセージ作成
# 取引失敗の場合
if uuid is False:
reply_message = MessageDefine.FAILED_TRADE_MESSAGE
# 取引成功でトレードが完了している場合
elif uuid == "":
reply_message = MessageDefine.APPLY_FOR_PURCHASE_MESSAGE + "\n\n" \
+ currency_pair + ": " + str(alt_quantity) + market_name.lower()
# 取引成功でトレードが完了していない場合
else:
reply_message = MessageDefine.APPLY_FOR_PURCHASE_MESSAGE + "\n\n" \
+ currency_pair + ": " + str(alt_quantity) + market_name.lower()\
+ "\n" + MessageDefine.TRADE_ID + " : " + uuid
# lineメッセージを返す
self.line_utils.line_reply(reply_token, reply_message=reply_message)
# 取引中の注文一覧をメッセージにして返す
def match_keyword_orders_list(self,reply_token):
order_list = self.bittrex_private.get_orders()
if order_list is None:
reply_message = MessageDefine.NO_ORDER_MESSAGE
else:
reply_message = MessageDefine.ORDER_LIST_MESSAGE + "\n\n"
# 所有しているコイン一覧を1行ずつメッセージに詰める
for item in order_list:
reply_message += str(item['market']) + ": " + str(item['quantity']) + "\n"\
+ "取引ID: " + str(item['uuid']) + "\n\n"
# lineメッセージを返す
self.line_utils.line_reply(reply_token,reply_message=reply_message)
# 取引中の注文一覧をメッセージにして返す
# line_message = "キャンセル <取引ID>"
def match_keyword_cancel(self, reply_token, line_message):
line_message_list = line_message.split()
uuid = line_message_list[1]
# 注文が存在するか確認
order_list = self.bittrex_private.get_orders()
if order_list is None:
reply_message = MessageDefine.NO_ORDER_MESSAGE
# lineメッセージを返す
self.line_utils.line_reply(reply_token, reply_message=reply_message)
else:
# 注文をキャンセルする
response = self.bittrex_private.order_cancel(uuid)
if response:
reply_message = MessageDefine.CANCEL_MESSAGE
else:
reply_message = MessageDefine.FAILED_CANCEL_MESSAGE
reply_message += "\n" + MessageDefine.TRADE_ID + " : " + uuid
# lineメッセージを返す
self.line_utils.line_reply(reply_token, reply_message=reply_message)
main.py
最初に呼び出され、受け取ったメッセージを判別し、適切な処理へ振り分ける
# -*- coding: utf-8 -*-
from define.key_word_define import KeyWordDefine # キーワード定義情報一覧
from utils.reply import reply #受け取ったメッセージを処理し、LINEでリプライを行うクラス
reply = reply()
# lineメッセージを受け取って起動する
def lambda_handler(event, context):
# debug
print(event)
print(event['body-json']['events'][0]['replyToken'])
print(event['body-json']['events'][0]['message']['text'].encode('utf-8'))
# lineイベントの取り出し
line_event = event['body-json']['events'][0]
# lineメッセージを返す用のトークン取得
reply_token = line_event['replyToken']
# 送られてきたlineメッセージ取り出し
line_message = line_event['message']['text'].encode('utf-8')
# [ヘルプ]のキーワードとマッチした場合
for word in KeyWordDefine.HELP:
if line_message.find(word) != -1:
reply.match_keyword_help(reply_token)
exit()
# [所有している通貨一覧を取得する]のキーワードとマッチした場合
for word in KeyWordDefine.WALLET:
if line_message.find(word) != -1:
reply.match_keyword_wallet(reply_token)
exit()
# [買い]のキーワードとマッチした場合
for word in KeyWordDefine.ORDER_BUY:
if line_message.find(word) != -1:
reply.match_keyword_buy(reply_token, line_message)
exit()
# [売り]のキーワードとマッチした場合
for word in KeyWordDefine.ORDER_SELL:
if line_message.find(word) != -1:
reply.match_keyword_sell(reply_token, line_message)
exit()
# [注文一覧]のキーワードとマッチした場合
for word in KeyWordDefine.ORDERS_LIST:
if line_message.find(word) != -1:
reply.match_keyword_orders_list(reply_token)
exit()
# [キャンセル]のキーワードとマッチした場合
for word in KeyWordDefine.ORDER_CANCEL:
if line_message.find(word) != -1:
reply.match_keyword_cancel(reply_token, line_message)
exit()
3. デプロイ
- 上記で作成したファイルをaws Lamdaへアップロード
- API gatewayとaws Lamdaを関連付け、デプロイ
- デプロイしたAPI gatewayのURLをMessaging APIと紐付け
- 完成\(^o^)/
作ってみて
メッセージ1つで取引できるのは、とても気分的に楽ですね。
このチャットのお陰で、ストレスフリーな売買ができるようになりました!
これからはチャートを見て、気軽に売買ができます!(๑•̀ㅁ•́๑)✧
参考:API gateway連携手順
更新
- ソースコードをGitに上げました。(2017/12/23)
- LINEとBittrexのアカウント作成とAPI key入手の手順キャプチャを追加しました。(2017/12/26)
- API gateway連携手順のスクリーンショットを追記しました。(2021/4/16)