追加情報(2023/06/24)
以下の投稿でa-Shellアプリでgitコマンドを利用する方法についてを投稿しました。
実際にgitが使えることが分かったので、python-bitbankccを使用するコードを下記のGitHubリポジトリにコミットしました。iCloudの共有リンクも記載していますので、詳細はREADME.mdを参照してください。
注意事項
※コードはa-Shellで使用する前提です。
※本記事に記載されたコードはgifの通り動作しますので変更はせずとします。
経緯
- bitbankでは公式のAPIを使用して認証情報をHTTPリクエストヘッダーに含めることでアセットの確認や新規注文をすることができますが、個人的にスマートフォンから指値注文する方法としてShortcutsアプリで実行する方法を検討していました。
- 調査したところ、Public & Private API をPythonで扱うライブラリ(python-bitbankcc)を使用することで指値注文ができること。また、ShortcutsアプリでPythonコードで実行するアプリとして、a-ShellとPytoの2つで実行できることが分かりました。
- そこで、本記事では調査内容について、実際にa-ShellとPytoでpython-bitbankccライブラリを使用して作成したbtc_jpyを指値注文するショートカットと合わせて紹介します。
- ShortcutsアプリでPythonコードを実行したいと考えている方の参考になればと思います。
記事について
- 実行環境は下記の通りです。
iOS 16.1.2
iPadOS 15.7 - APIの仕様(Public,Private,APIキー等)についてはbitbank.cc 公式APIドキュメントやこちらの方の記事にてわかりやすく紹介されていますので割愛します。
- 記載されているコードは必ずしも期待された動作を保証するわけではありません。
- 本コードを実行したこと、参考にしたことによって被るあらゆる損害について責任を負いかねますので自己責任でご利用ください。
ShortcutsアプリでPythonコードを実行する方法について
AppStoreにはPythonコードをiOSで実行するアプリがあります。
アプリにはShortcutsと連携をサポートしている場合、ショートカット内のアクションとしてPythonコードを実行することができます。調べるとa-ShellとPytoの2つがありました。
もしかすると他にも使用できるアプリはあるかもしれませんが、本記事ではa-ShellとPytoの2つのアプリを使用して作成したショートカットを紹介します。
アプリの特徴について
a-Shell
詳細は公式サイトを参照してください。主な特徴は下記の通りです。
- 価格は無料
- Pythonではpipコマンドを使用可能
- venvによる仮想環境作成が可能(詳細)
- 2022/12/17時点のpythonのバージョンは3.11.0
$ python -V
Python 3.11.0
- ShortcutsアプリからPythonコードを実行する場合はa-Shellのコマンドを実行アクションでコマンドライン指定で実行する。
- Shortcutsアプリでは値を参照する際は標準出力結果、関数の戻り値を参照できないため後述するファイル書き出し処理を行うことで参照することができる。
- a-Shellアプリではコマンドを実行後にShortcutsアプリに遷移する設定がないため、コマンドを実行アクションで
open shortcuts://
を実行することで遷移することができる。(詳細)
Pyto
詳細は公式サイトと公式ドキュメントを参照してください。主な特徴は下記の通りです。
- 価格は有料(3日間のお試し利用あり。外部モジュールを使用する場合はFull Versionの購入が必要。)
- Pythonコードはコマンドライン指定かファイル起動画面からRun with argumentsを選択して実行する。
- 2022/12/17時点のpythonのバージョンは3.10.0
- pipコマンドが使用可能(PyPIの管理画面あり)
- venvは使えない?(venvコマンドでエラーは出ませんが、
ENV_DIR
で指定するターゲットディレクトリは作成されませんでした。) - ShortcutsアプリからPythonコードを実行する場合はコードをShortcutsアプリ内に記述するRun Code指定、またはスクリプトを指定するRun Script(引数指定可能)アクションで実行する。
- Shortcutsアプリで値を参照する際はコード側で
print関数
で標準出力し、Get Script Outputアクションで実行する。
作成したもの
bitbankのAPIを使用してbtc_jpyを指定価格で指値注文するショートカットを作成しました。
ショートカットではbitbankの指値注文結果(json)を受けて表示したいデータを取得して表示しています。
なお、レスポンスデータはAPIドキュメント記載の新規注文を行う / Responseに従い送信されます。タイムスタンプはunixtimeのためローカル時間に変換しています。
表示したいデータはコード側でまとめて必要なデータを取得する形式にするとアクションが複雑にならずに済みます。
a-Shellのアクションを使用したショートカット
Pytoのアクションを使用したショートカット
仕様とソースコードについて
- ショートカットアプリのアクションとソースコード(注文処理)の流れをアクティビティ図で作成しました。
緑の部分はa-ShellとPytoで異なる部分になります。 - 価格情報はa-Shell,Pyto共に入力を要求アクションで数字で入力し、コマンドライン引数指定でPythonコード側で参照して処理します。
- Pythonコードでは価格情報として現在の買い注文の最高値を取得し、指値注文はPublic & Private API をPythonで扱うライブラリであるpython-bitbankccと認証時に指定するAPIキーとAPIシークレットキーを環境変数として設定するためにpython-dotenvモジュールを使用します。認証情報は端末内で保存することでセキュリティに配慮しています。
- python-bitbankccとpython-dotenvは標準では組み込まれてはいないため、pipコマンドでインストールする必要がありました。python-bitbankccはPyPIには登録されておらず、また、a-Shell,Pyto共にgitコマンドをサポートしていなかったため、python-bitbankccのGitHubページに記載されたインストール手順を行うことができませんでした。そこでPC側でgit cloneしたコードをiPhoneに送ることで対応しました。
- [2023/06/24追記] a-Shellでは可能でした。(詳細)
- python-dotenvはa-Shellでは
pip install python-dotenv
、PytoではPyPlページで検索してinstall可能です。 -
注文数量は現在の買い注文の最高値と購入したい価格から計算して注文用のリクエストデータとして作成しますが、実装の都合で注文時に即約定しないようにコードでは現在の買い注文の最高値から
ADJUST_PRICE
という定数分引いた金額を基準に計算しています。(定数は0とすれば現在の買い注文の最高値で注文数量を計算します。)
ファイル構成について
git cloneしてファイルコピーしたpython-bitbankccに[追加]のファイルを格納します。
python-bitbankcc % tree -t -a -L 2
.
├── .gitignore
├── LICENSE
├── README.md
├── setup.cfg
├── setup.py
├── python_bitbankcc
│ ├── __init__.py
│ ├── private_api.py
│ ├── public_api.py
│ └── utils.py
├── .git(省略)
├── .env //[追加]APIキーとAPIキーシークレットを環境変数として定義
├── response.txt //a-Shellでコード実行時に作成するため追加不要
└── input_spot_order_env.py //[追加]指値注文用のファイル
a-Shellの場合
値を参照する際は標準出力結果、関数の戻り値を参照できないため、ファイル(response.txt)にopen()関数
の引数mode
を'w'
で指定して上書き(既存の内容は削除)で保存します。その後、open shortcuts://
コマンドを実行することでShortcutsアプリに遷移し、フォルダからファイルを取得アクションを実行してファイルのデータを読み込みます。
アクティビティ図
ソースコード
import sys, os, json, datetime
from python_bitbankcc import public, private
from dotenv import load_dotenv
class UseBitbankApi:
def __init__(self):
# コマンドラインより送信するパラメータを設定する
self._args = sys.argv
# 要素数で判別する
if(len(self._args) == 1):
# 引数指定がない場合は500円で固定する
self._adjust_order_num = 500
elif(len(self._args) > 2):
# 長さが2より大きい場合は価格の指定誤りとしてプログラム終了
# ただし、ショートカットから実行する場合は通らない
print('価格は一つのみ指定してください')
sys.exit()
else:
# コマンドライン引数で指定された数字で設定する
self._adjust_order_num = int(self._args[1])
# 指値取引のために最新取引価格から調整するための価格(値は0以上で指定してください。)
self._adjust_price = 5000
# BTC取引時の小数点以下の桁数
self._decimal_digits_btc = 8
# API設定
load_dotenv()
self._api_key = os.environ['API_KEY']
self._api_secret = os.environ['API_SECRET']
# public API classのオブジェクトを取得
self._pub = public()
# private API classのオブジェクトを取得
self._prv = private(self._api_key, self._api_secret)
# public APIを使用してティッカー情報を取得するメソッド
def get_ticker(self,ticker_ptn,pair):
value = self._pub.get_ticker(pair)
return value[ticker_ptn]
# 注文価格と注文数量を返すメソッド
def set_order_data(self,ticker_ptn,pair):
buy_price = int(self.get_ticker(ticker_ptn,pair))
order_num = (self._adjust_order_num / (buy_price - self._adjust_price))
order_num = round(order_num, 8)
order_price = buy_price - self._adjust_price
return order_num,order_price
# private APIを使用して指値注文するメソッド
def spot_order(self,ticker_ptn,pair):
order_num,order_price = self.set_order_data(ticker_ptn,pair)
order_num = str(order_num)
#print('order_num =',order_num)
order_price = str(order_price)
#print('order_price =',order_price)
response = self._prv.order(
pair, # ペア
order_price, # 価格 (成行注文の場合は None にする)
order_num, # 注文枚数
ticker_ptn, # 注文サイド (buy|sell)
'limit', # 注文タイプ (limit|market|stop|stop_limit)
# 以降は任意の引数
False, # post_only 注文、デフォルトは False, None も可能で Falseと同じ挙動
False # trigger_price 逆指値などのトリガー価格
)
return response
# 注文日時(UnixTimeのミリ秒)をローカル時間に変換するメソッド
def conv_unixtime(self,unixtime):
data = unixtime / 1000
dt = datetime.datetime.fromtimestamp(data).strftime('%Y/%m/%d %H:%M:%S')
return dt
# ファイルを書き出すメソッド(a-Shellの場合に使用する)
def file_write(self,data):
f = open('./response.txt', 'w')
f.write(data)
f.close()
if __name__ == '__main__':
# インスタンス生成
use_bitbank_api = UseBitbankApi()
# 指値注文時の例外処理判定
try:
# btc_jpy指定で指値注文する
response = use_bitbank_api.spot_order('buy','btc_jpy')
except Exception as e:
# エラーメッセージをファイル出力する(a-Shellの場合)
use_bitbank_api.file_write(str(e))
else:
# 注文日時(UnixTimeのミリ秒)をローカル時間に変換する
response['ordered_at'] = use_bitbank_api.conv_unixtime(response['ordered_at'])
# jsonエンコード
response_json = json.dumps(response)
# レスポンスデータをファイル出力する(a-Shellの場合)
use_bitbank_api.file_write(response_json)
Pytoの場合
PytoではショートカットでGet Script Outputでレスポンスデータを参照するためにprint()関数で標準出力しています。ファイル出力処理とprint()関数以外は同じ処理を実装しています。
アクティビティ図
ソースコード
import sys, os, json, datetime
from python_bitbankcc import public, private
from dotenv import load_dotenv
class UseBitbankApi:
def __init__(self):
# コマンドラインより送信するパラメータを設定する
self._args = sys.argv
# 要素数で判別する
if(len(self._args) == 1):
# 引数指定がない場合は500円で固定する
self._adjust_order_num = 500
elif(len(self._args) > 2):
# 長さが2より大きい場合は価格の指定誤りとしてプログラム終了
# ただし、ショートカットから実行する場合は通らない
print('価格は一つのみ指定してください')
sys.exit()
else:
# コマンドライン引数で指定された数字で設定する
self._adjust_order_num = int(self._args[1])
# 指値取引のために最新取引価格から調整するための価格(値は0以上で指定してください。)
self._adjust_price = 5000
# BTC取引時の小数点以下の桁数
self._decimal_digits_btc = 8
# API設定
load_dotenv()
self._api_key = os.environ['API_KEY']
self._api_secret = os.environ['API_SECRET']
# public API classのオブジェクトを取得
self._pub = public()
# private API classのオブジェクトを取得
self._prv = private(self._api_key, self._api_secret)
# public APIを使用してティッカー情報を取得するメソッド
def get_ticker(self,ticker_ptn,pair):
value = self._pub.get_ticker(pair)
return value[ticker_ptn]
# 注文価格と注文数量を返すメソッド
def set_order_data(self,ticker_ptn,pair):
buy_price = int(self.get_ticker(ticker_ptn,pair))
order_num = (self._adjust_order_num / (buy_price - self._adjust_price))
order_num = round(order_num, 8)
order_price = buy_price - self._adjust_price
return order_num,order_price
# private APIを使用して指値注文するメソッド
def spot_order(self,ticker_ptn,pair):
order_num,order_price = self.set_order_data(ticker_ptn,pair)
order_num = str(order_num)
order_price = str(order_price)
response = self._prv.order(
pair, # ペア
order_price, # 価格 (成行注文の場合は None にする)
order_num, # 注文枚数
ticker_ptn, # 注文サイド (buy|sell)
'limit', # 注文タイプ (limit|market|stop|stop_limit)
# 以降は任意の引数
False, # post_only 注文、デフォルトは False, None も可能で Falseと同じ挙動
False # trigger_price 逆指値などのトリガー価格
)
return response
# 注文日時(UnixTimeのミリ秒)をローカル時間に変換するメソッド
def conv_unixtime(self,unixtime):
data = unixtime / 1000
dt = datetime.datetime.fromtimestamp(data).strftime('%Y/%m/%d %H:%M:%S')
return dt
if __name__ == '__main__':
# インスタンス生成
use_bitbank_api = UseBitbankApi()
# 指値注文時の例外処理判定
try:
# btc_jpy指定で指値注文する
response = use_bitbank_api.spot_order('buy','btc_jpy')
except Exception as e:
# エラーメッセージ標準出力(Pytoの場合)
print(e)
else:
# 注文日時(UnixTimeのミリ秒)をローカル時間に変換する
response['ordered_at'] = use_bitbank_api.conv_unixtime(response['ordered_at'])
# jsonエンコード
response_json = json.dumps(response)
# shortcutアプリでpytoの実行結果を取得(Get Script Output)するためには
# print関数で出力する(Pytoの場合)
print(response_json)
まとめ
- bitbankのAPIを使用してbtc_jpyの価格を参照し、指定価格で指値注文するPythonコードを作成し、そのコードをShortcutsアプリから実行することで指値注文を行うショートカット(a-Shell,Pyto)について紹介しました。
- a-ShellではShortcutsアプリとの連携のしづらさはありますが、無料であり、Python以外のコードも実行できるため十分な機能をサポートしていると思います。
- PytoではShortcutsアプリ用に入力と出力に対応したアクションがあるためショートカットを作成しやすい点がありました。アプリは有料ですが、Pyto LibraryやExample等、ドキュメントがまとめられているため実装のしやすさはあります。
- 本記事では指値注文のコードを実行していますが、オートメーションと連携すれば定期実行することで積立にも対応できると思います。
- その他、指値注文するコードとしてGoogle Apps Scriptベースのコードも作成していますので、興味があれば確認してみてください。(GitHub / gasBitbankSpotOrder)