【Python】並行処理のおべんきょうの参考
参考として先人の記事のリンクを貼る
動かし方
qiita.com
https://qiita.com/__init__/items/74b36eba31ccbc0364edqiita.com
注意すべき点
qiita.com
並行処理関連の公式ドキュメント
docs.python.jp
試しに、5秒ごとに目的の動作を行わせる
import datetime import time import threading import queue def func1(event): while(1): event.wait() print('hoge') event.clear() def func2(event): while(1): event.wait() print('fuga') event.clear() def timer(event): while(1): time.sleep(1) now=datetime.datetime.now() print(now) if now.second%5==0: event.set() if __name__ == "__main__": event = threading.Event() t1 = threading.Thread(target=func1, args=(event,)) t2 = threading.Thread(target=func2, args=(event,)) t3 = threading.Thread(target=timer, args=(event,)) t1.start() t2.start() t3.start()
結果、5秒に1回 hoge fuga する
2019-02-26 00:07:32.680651 2019-02-26 00:07:33.700176 2019-02-26 00:07:34.703568 2019-02-26 00:07:35.704306 hogefuga 2019-02-26 00:07:36.713224 2019-02-26 00:07:37.718298 2019-02-26 00:07:38.732420 2019-02-26 00:07:39.739030 2019-02-26 00:07:40.753466 fugahoge 2019-02-26 00:07:41.767390 2019-02-26 00:07:42.777210 2019-02-26 00:07:43.786152 2019-02-26 00:07:44.799364 2019-02-26 00:07:45.815294 hogefuga 2019-02-26 00:07:46.828104 2019-02-26 00:07:47.832805 2019-02-26 00:07:48.848034 2019-02-26 00:07:49.858399 2019-02-26 00:07:50.863243 hogefuga 2019-02-26 00:07:51.865927 2019-02-26 00:07:52.879863 2019-02-26 00:07:53.895192 2019-02-26 00:07:54.898684 2019-02-26 00:07:55.899330 fugahoge …
以後適宜追記
ZOZOの前澤社長がどのように100人抽選したかに関する一考察
※1/8 16時 表記の修正/1.2節補足/1.4節の追加/おわりに補足
はじめに
ZOZOの前澤社長の以下のツイートが年明け早々TLに溢れました。
1/8に当選者へのDMが送られたようですが、もしマトモに抽選したとしたら、どのようにこれだけ膨大な数の参加者から抽選したのでしょうか?
ZOZOTOWN新春セールが史上最速で取扱高100億円を先ほど突破!!日頃の感謝を込め、僕個人から100名様に100万円【総額1億円のお年玉】を現金でプレゼントします。応募方法は、僕をフォローいただいた上、このツイートをRTするだけ。受付は1/7まで。当選者には僕から直接DMします! #月に行くならお年玉 pic.twitter.com/cKQfPPbOI3
— Yusaku Maezawa (MZ) 前澤友作 (@yousuck2020) 2019年1月5日
時間制約について
以下のツイートから、締め切りから抽選まで9時間以内に終えたことがわかります。
たった今お年玉キャンペーン終了しました。多くのご応募ありがとうございました。この後抽選作業に入り、明日の朝9時頃から当選者へ順次DMにて僕よりご連絡させていただきます。楽しみにお待ちください。 #月に行くならお年玉
— Yusaku Maezawa (MZ) 前澤友作 (@yousuck2020) 2019年1月7日
おはようございます。
— Yusaku Maezawa (MZ) 前澤友作 (@yousuck2020) 2019年1月8日
当選者へのDM配信開始します!
100名に送り終えるのに数時間かかると思います。
楽しみにお待ちください!!
1. 選別せずに抽選した場合
該当のツイートをRTした人を列挙し乱数とかで抽選すれば公平そうです。ただこれだけ膨大になるとRTした人をどうやって抽出したのかが気になるところです。まずはRTした人を選別せずに抽選した場合について、以下のような仮説を検証していきます。
1.1 該当ツイートをRTした人のリストを直接入手した?
だれがRTしたかわかればそれが一番楽ですが、公式アプリやWebクライアントだと100人程度が表示限界のようです。そこでTwitterのapiを使って入手できないかと考えます。しかし、apiでも直近100ユーザー分のidしか取得できないようです。*1
1.2 フォロワーのリストを入手してから該当ツイートをRTしているか調べる?
1/8 12時時点で616万フォロワーですので、フォロワーの全貌を把握するのは難儀かと思いますが、一応このような手法も考えられます。フォロワーを取得するapiが存在し、1回のリクエストで5000 ids入手できます。*2 リミットは 15 times/15 minですので、5000 ids/min入手できます。これだとフォロワー全部取得するまで 1232 min = 20.5 h かかるので、時間制限に引っ掛かります。ここから各ユーザーのタイムラインを取得して該当ツイートが含まれているか(RTしたツイートが含まれている)調べることになるので、さらに時間がかかります。
フォロワーの取得を応募開始から随時行っていたとしたら、猶予が2日くらいに伸びます。その場合、フォロワーそれぞれについて、該当ツイートをRTしているか確かめます。あるユーザーのツイートを得るapiでは、リミットが900 times/15 minなので、60 times/minの頻度で確認できます。*3 この場合全フォロワーについて直近のタイムラインに該当ツイートのRTが含まれているか確かめることになります。しかし全フォロワーを終えるのに102,666 min = 71.2 dayかかります。また、1回のリクエストでは直近200ツイートまでしか取得できないので、ヘビーユーザーも除外されます。
1.3 ツイートを検索する?
[Twitter API] リツイートを100件より多く取得する方法 – プログラミング生放送
上記サイトにて紹介されていた方法では、1.1で述べた100ユーザー制限を突破できます。結果からidを抽出すればRTしたユーザーのリストが作れます。1回の検索で100件取り出せ、リミットは180 times/15 minですので、1200 ids/min入手できます。1/8 12時時点で該当ツイートのRT数は564万ですので、これだとRTしたユーザーをすべて取得するのに 4700 min = 78.3 h かかり、時間制限に引っ掛かります。
1.4 ユーザーidを乱数で生成し、そのユーザーがフォロワーかつ該当ツイートをRTしているか調べる
学科民をはじめとする方々から上記の意見をいただいたので追記します。
1.3までは、あてはまるユーザーを探しにいくという発想でしたが、逆にユーザーをランダムに選んでそのユーザーが当てはまっているか調べるという発想です。2018年10月時点でTwitterのアクティブユーザー総数は3億2600万、うち日本のユーザーは4500万。*4 また、1/8時点で前澤社長のフォロワー数は約600万です。したがって、全ユーザーの1.8%が前澤社長のフォロワーということになります(!?)。また、該当ツイートのRT数が1/8 12時時点で564万であり、仮にすべてフォロワーによるRTとすれば、この564万ユーザーを引き当てられればよいということになります。
ただ、これは現時点で生きているアカウントの総数であり、今までに数多くのアカウントが消えているため、もしランダムにidを指定しても生きているアカウントにヒットするかはわかりません。また、Twitterのユーザーidの生成規則を調べてみたのですが、明快な答えが得られずじまいでした。例えばTwitterのオフィシャルアカウント(@Twitter)のユーザーidは783214、自分のアカウント(2012/8作成)のユーザーidは9桁、最近作成されたアカウントだとユーザーidが10桁になっているようです。9桁対応でidを生成するのであれば条件に適合するユーザーへのヒット率は0.56%ですが、10桁対応するとヒット率が0.056%に落ちます。
生成されたidのアカウントがフォロワーかつ該当ツイートRT済みであることを確認するためには、手作業だと面倒なのでやはりapiに頼ることになります。まず社長をフォローしているか確かめます。あるユーザーのフォローリストを得るapiでは、リミットが15 times/15 minなので、1分に1ユーザーしかチェックできません。*5 RTしているか調べるのは、1.2節で述べたのと同様のapiを用います。
有効な100人を得るために、その100倍以上のidを生成して確かめなければいけないので、apiでやるには意外と時間がかかりそうです。もしこの手法を採用するなら、id生成した後は社員がやるのかもしれません。
2. リプライを送った人に限定して抽選した場合
どうも1の手法だと時間制限に引っ掛かるっぽいので、リプライした人限定なのかもしれません。該当ツイートにリプライしたユーザーを列挙する手法について考えます。
ところが、「あるツイートに対するリプライ」を拾ってくるapiがないらしい。あるのは「あるユーザーに対するリプライ」を拾ってくるapiのみ。*6
該当ツイートについているリプライ数は39万、また該当ツイート以降のツイートについているリプライ数は、足し合わせても10万程度という感じです。したがって約50万のリプライのデータからidを抽出すればよいことになります。1回のリクエストで200件のリプライが取得でき、リミットは75 times/15 minですので、1000 replys/min入手できます。50万リプとして全所要時間は 500 min = 8.3 hですので、時間制限に引っ掛かりません。
リプしてきた人を重複を取りのぞいて100人抽選し、その人が当該ツイートをRTしていなかったり、フォロワーでなかったりしたら、追加で抽選していく感じになるかと思います。
おわりに
ちょっと検索してみると、実際に当選したのは具体的な目標を語ったリプを送った人が多いようです。届くDMにも、目標を応援している旨のメッセージが書かれているようです。最初は完全にランダムでやるつもりが、予想外に伸びてしまったため、急遽リプを送った人から選ぶ形に変えたのかもしれません。
また、Rate Limiting — Twitter Developersを見ると、
Standard API endpoints only, does not apply to premium APIs
と書いてあります。Twitter Developersに課金すれば、ここまで述べてきたしょうもないリミットを回避することができるわけです。金は正義っぽい。
この企画に便乗してバラマキ企画をしている方で、本当にばらまく意思があって、課金もしたくない方は、上記の手法からお選びください(いるのか?)。
BitmexRektを題材に Tweetの取得・可視化・BTC取引botの話
この記事は eeic Advent calender 2018 その2 の第5日目の記事になっています。
4日目 EEIC走馬灯(落第学生三番煎じver) - acryloshobonileのブログ
6日目 eeic走馬灯(デバイス系かつ怠惰学生ver.)前編 - 自由帳
先輩方の走馬灯の間にお邪魔してすみませんというお気持ち…。
(強電系走馬灯、来年以降書いてみたい)
- 0. はじめに
- 1. BitmexRektについて
- 2. Twitterのapiを使うために
- 3. ツイートの取得
- 4. 実際の値動きとともにグラフを描画
- 5. 取引 bot 化に向けたバックテストと課題
- 6. 取引 bot のフォワードテスト
- 7. おわりに
0. はじめに
電気電子工学科 B3 の者です。プログラミングは学科に入ってから始めました。普段は電力系の授業を受けたり実験をしたりしています。今回のコレはおべんきょうのための個人的な試みですが、アドベントカレンダーその2が空いてそうだったので記事としてまとめ、投稿させていただきます。
バックテストまでの全体のコードはGitLabにあげておきます。メインの処理は ipynb ファイルに記述しています。上手くないのでデータ保持の形式まわりがアレですが…。
1. BitmexRektについて
BitMEXとは、仮想通貨のレバレッジ取引を行う取引所です。Bitcoin をはじめいくつかの通貨が扱われており、取引量は世界最大級です。
一般にレバレッジ取引においては、ロスカットと呼ばれる自動精算システムがあります。(証拠金維持率)=100×(総資産評価額)/(必要証拠金) と定義し、証拠金維持率が一定の水準を下回ると、ロスカットが行われます。BitMEX では、ポジションが逆行して証拠金維持率が0%になるとロスカットされます。
BitmexRektとは、BitMEX において行われるロスカットを監視し、ツイートする bot のことです(図1)。ロスカットは一方的で大きな値動きがあるときに発生しやすいため、ロスカット発生情報は取引の参考とされています。
今回はこの BitmexRekt のツイートを取得し、得たデータをいじっていきます。
2. Twitterのapiを使うために
2018年7月ごろに Twitter の api 制限が厳格化され、api を使うには Developer account としての登録が必要になりました。ここ で述べていますが、英作文をしなきゃいけないのでちょっと面倒です。アカウントを登録したら、app 登録を行って、api key など4種類取得します。
3. ツイートの取得
処理をpython-twitterというライブラリにすべてお任せします。
GetUserTimeline という関数を使うと、あるユーザーのツイートを取得することができるようなので、それを使います。
ツイートには id が振られており、それをもとに取得するツイートの範囲を指定することもできます。引数 max_id を使うと、そのid以前のツイートのみ取得することができます。
その他の機能については公式のドキュメントを参照してください。
今回は XBT/USD という Bitcoin 無期限取引のペアのみ抽出することとします。
Liquidated long は買い注文が清算されたことを表し、価格の下落が起きていることを示唆します。
Liquidated short は売り注文が清算されたことを表し、価格の上昇が起きていることを示唆します。
ロスカットが発生した時刻、方向、枚数を1セットのデータとして保存します。ここで方向については、買い注文の清算を-、売り注文の清算を+と定めます。
実際のコードは以下のように適当に。1サイクルで200ツイートが取得上限です。とりあえず600ツイート手に入れます。各種 key は予め読み込んでおきました。
api = twitter.Api(consumer_key=CONSUMER_KEY, consumer_secret=CONSUMER_SECRET, access_token_key=ACCESS_TOKEN_KEY, access_token_secret=ACCESS_TOKEN_SECRET) liquidated_list=[] last_id=0 for i in range(3): if i==0: tweets = api.GetUserTimeline(screen_name="BitmexRekt",count=200) else: tweets = api.GetUserTimeline(screen_name="BitmexRekt",max_id=last_id-1,count=200) for tweet in tweets: word_list=tweet.text.split() if 'XBTUSD:' in word_list: if word_list[1]=='long': amount=(-1)*int(word_list[5].replace(",","")) at_price=float(word_list[7]) liquidated_list.append([tweet.created_at,amount,at_price]) elif word_list[1]=='short': amount=int(word_list[5].replace(",","")) at_price=float(word_list[7]) liquidated_list.append([tweet.created_at,amount,at_price]) last_id=tweet.id #以下は word_list の format #Liquidated short on XBTUSD: Buy 11,305 @ 3823.5
こうするとこんな感じのデータが格納されます。時刻、清算枚数、清算価格の順になっています。
['Thu Dec 13 09:04:55 +0000 2018', 47757, 3397.5], ['Thu Dec 13 09:04:50 +0000 2018', 83828, 3393.5], ['Thu Dec 13 09:04:44 +0000 2018', 43445, 3387.0], ['Thu Dec 13 09:04:37 +0000 2018', 225990, 3383.5], ['Thu Dec 13 09:04:32 +0000 2018', 223023, 3377.0], ['Thu Dec 13 08:27:10 +0000 2018', -499, 3358.0], ['Thu Dec 13 07:59:55 +0000 2018', 58351, 3374.5], ['Thu Dec 13 07:59:50 +0000 2018', 2682, 3373.5], ['Thu Dec 13 06:00:05 +0000 2018', 32335, 3368.0], ['Thu Dec 13 06:00:02 +0000 2018', 36405, 3364.0], ['Thu Dec 13 05:59:47 +0000 2018', 5010, 3360.5], ['Thu Dec 13 05:59:41 +0000 2018', 107903, 3358.5], ['Thu Dec 13 05:51:39 +0000 2018', -35209, 3338.5], ['Thu Dec 13 05:50:29 +0000 2018', -6002, 3342.5], ['Thu Dec 13 05:50:22 +0000 2018', -158472, 3344.5], ['Thu Dec 13 05:29:11 +0000 2018', -72500, 3346.5], ['Thu Dec 13 05:29:04 +0000 2018', -68622, 3348.0],
4. 実際の値動きとともにグラフを描画
cryptowatch の api を使うと各取引所の直近の価格データを手に入れることができます。
価格データの抽出は get_ohlcv.py の中に記述してあります。取引所と単位時間(1min, 3min, 5min, 15min, 30min, 60min 等)を与えると時刻、始値、高値、安値、終値を返す関数を用意してあります。
※12/18 追記
稀に欠損値 all 0 が返ってくることがある模様…?
取得したツイートから得たロスカット情報は、longとshortに分けて保持します。
また、グラフの開始時刻を価格データのスタート時刻にするため、それより昔のロスカットデータを捨てて整理します(価格データは1分足だと500分ぶんしかないので、だいたいロスカットデータのほうが余る)。
この辺の実装は冗長なだけなので具体的なコードは割愛します。
価格とロスカット情報を2段に分けてプロットさせると図 2 のようになります。青は long のロスカット、赤は short のロスカットを表しています。2段目のグラフの縦軸は清算枚数を表しています。おおまかに、価格の下落により long のロスカットが発生し、上昇により short のロスカットが発生している様子が見て取れます。
また、価格とロスカット情報を1つにまとめてプロットさせると図 3, 4 のようになります。ロスカット情報を表す点は、濃度が清算枚数と対応しています。これを見ると価格の上下とロスカットの対応の様子がより分かりやすいかと思います。
5. 取引 bot 化に向けたバックテストと課題
4まででは、過去のロスカットを抽出し可視化してみました。
ここで、リアルタイムでロスカットツイートを取得することにより、取引 bot のシグナルとして利用できるのではないかという目論見が立ちます。
そこで、得たロスカットデータをもとに、簡単なロジックのバックテストを行ってみました。
バックテストのコードもメインの ipynb ファイル内に記述してあります。
取引ルールは以下のようにします。
long のロスカットが発生したら short で entry、short のロスカットが発生したら long で entry。
価格は、ロスカットが発生した時刻の分足の終値で entry、次の分足の終値で exit とします。
コードでは、この辺の処理がしやすいように、途中のデータ整理で時刻の秒を切り捨てています。
(ex. hh:mm:ss = 19:22:30 にロスカットが発生したら、19:22の分足の終値で entry、19:23の分足の終値で exit する。言い換えれば 19:23:00に entry、19:24:00に exit)
ここで、
profit factor = (総利益)/(総損失の絶対値)
max drawdown : グラフの最大へこみ
と定義します。
PF は大きいほうがよく、drawdown の絶対値は小さいほうがよいです。
まずはロスカットデータの元である取引所 BitMEX でのバックテスト結果を2例示します(図5, 6)。
500分ぶんの価格データを利用したバックテストで、図 5 と図 6 はそれぞれ別の日に採取したデータを用いています。
ロスカットが多いときはモリモリ、少ないときも着実に積み重ねている様子が見て取れます。
次に、国内の取引所 bitFlyer のレバレッジ取引価格データを使ってバックテストします。
シグナルは BitMEX のロスカットデータですが、価格は相互に追従しあうものだと思われるため、試してみました。
結果を2例示します(図7, 8)。グラフはBitMEXのバックテスト結果とよく似ています。強いですね。
しかし、果たしてそうでしょうか。
おそらく、実際にこのロジックを bot 化して運用しても、ここまでうまくはいかないのではないかと思われます。以下に述べるような問題点があるためです。
- ロスカットが発生するような場面では、サーバが遅延する
実際に注文を出す際には、注文を出した瞬間の価格で必ず約定するわけではありません。ある程度の価格のブレがあり得ます。
特に、ロスカットが発生するような場面では、価格の激しい上下動があり、注文数も増加するため、取引所のサーバの遅延が起きます。
今回のバックテストでは、必ず終値で約定するとしているため、この影響は無視できないわけです。
- 売り板と買い板のスプレッド損
売りたい人は少しでも高く売りたい、買いたい人は少しでも安く買いたい、それゆえ売り板と買い板には価格差が生じます。
この価格差はスプレッドと呼ばれます。取引所を実際に眺めてみるとわかりやすいかと思います。
実際に entry する場合には、成行注文/指値注文のどちらかを選択して注文することになります。
成行注文とは、価格はいくらでも構わないので買わせてくれ/売らせてくれ、という注文方式です。
すぐに約定する代わりに、先ほど述べたスプレッドぶん不利な価格で売買していることになります。
指値注文とは、価格を指定して注文する方式です。
スプレッドによる不利益はなさそうですが、約定せずにさらに価格が上昇/下落して、チャンスを失ってしまう可能性があります。
バックテストでは、この影響を無視して必ず終値で約定するとしていましたので、実際の結果はこの影響を受けて変わってくるでしょう。
あくまでもデータで遊んでみた!という体験をまとめたものですので、これを鵜呑みにして取引されても一切責任はとりません。
※12/16 追記
フォワードテストもやってみているので、その結果も書くかもしれない。
※12/20 追記
フォワードテスト第1弾、書いた。
6. 取引 bot のフォワードテスト
せっかくなので、BitMEXのロスカットデータを生かした bot を作ってみます。
bitFlyer でのレバレッジ取引です。python で書いて AWS Cloud9 上で動かしています。
pybitflyer や ccxt を使ってちょっと慣れればシンプルな売買はすぐ実装できます。
先駆者の数多くの指南記事がありますので、興味があればググってみてください。
第 1 弾のロジックは、5 章でバックテストしたものを微調整して、以下の通りとしました。
- 取引ロットは 0.05 枚で固定
- 1 分ごとにロスカットデータを取得・集計。
- entry は指値、exit は成行(entry で指値注文が約定していれば)
- 新規ロスカット発生分で entry し、ロスカットが終息したら exit
(例)
時刻 | ロスカット | 取引 |
22:13:00~22:13:59 | 0 | |
22:14:00~22:14:59 | -200 | |
22:15:00~22:15:59 | -300 | 22:15:00 short entry |
22:16:00~22:16:59 | 0 | short hold |
22:17:00~22:17:59 | 0 | 22:17:00 short exit |
entry を指値にしたのは、最初に entry と exit どちらも成行にしたものを組んで回してみた結果、あまりに危なっかしかったからです。
また、ロスカットが続いている間は exit しないことに決めたのは、取引回数が増えることによる成行スプレッド損を軽減するためです。
たぶんロスカットが続いている間は利益が出る方向に価格が推移しているので、ポジションを持ちっぱなしでも大丈夫だと考えました。
図10のように、勝とうが負けようが bot は淡々と取引を行っていきます。
図 11 に、実際の損益推移を示します。ログインボーナスでもらえる微小な BTC 現物の価格変動の影響があるため、bot が止まっているときも損益変動が少しあります。
バックテストではドローダウンがほとんどなかったのに、この結果を見ると命からがら生き延びているだけという感じがします。
ロットを 0.05 としているので、バックテストの結果のおよそ 1/20 が 500 分で得られる収益の理論値なわけですが、とてもじゃないがそのレベルに到達していないようです。
実際に bot を回してみると、サーバーが注文を受け付けなかったり、ラグがあったり、価格が上下にブレて不利な約定価格になっていたり、といった問題に直面します。
これを書いている間に、所有ポジションをチェックするあたりで書き換えるべき点が見つかったので、もうすこし改善の余地はありそうです。
今後もロスカットデータを生かした bot のアイデアを試していきます。良さげなモノがあればここに追記していきたいと思います。
7. おわりに
昨年の今頃は少なからぬ人々がビットコインだビットコインだと言っていた印象ですが、今年はモリモリ下り坂です(図9)。
ただ、取引所を筆頭に api をモリモリ公開してくれたり、ccxt をはじめとする優秀なライブラリがあったりするおかげで、プログラミングの経験が浅い自分のような人でも、おべんきょうの題材とするのには悪くないんじゃないかと感じています。また、自分で組んだ bot が健気に動いている様子を眺めていると、結果がショボかろうと愛しさが湧いてきます。おもしろいものです。
日々控室で同期を見ていると、この学科には様々な分野に長けた強い人が多いなぁと感じます。
そんな強い人々が取引 bot 作りに手を出したらきっとすごいことになるんじゃないかと思いつつ、筆をおきます。
最後までお付き合いいただきありがとうございました。
Twitter apiを使うためにDeveloper accountを申請する
2018/07からTwitter apiの使用が厳しくなり、ちょっと検索apiを叩こうにもDeveloper accountを作らなければならなくなった模様。
apiに関するツイートを検索してみると、厳格化に対する嘆きが散見される。
しかし、やりたいことがちょっと思いついてしまったので、やるしかない。
とりあえず申請。
"Twitter api"でサーチすると(たぶん)一番上に出てくるDeveloperページからapplyを選択し、順に次のような項目を入力。
・User profile
どのアカウントと連携させるか?
・Account details
組織or個人(非商用)の選択
・Use case details
どのような目的でapiを利用するのか選択
さらに300文字以上の英作文
1.apiの使用目的
2.コンテンツの分析の有無と分析手法
3.自発的なツイート・リツイート・いいねの有無
4.取得したデータを具体的に表示するか統計的に処理して表示するか
申請が通ったら更新する。
追記
1時間もしないうちに許可された わいわい!
cryptactの未分類取引と格闘する
はじめに
cryptactは、仮想通貨取引における煩雑な税金計算をアシストしてくれる無料ツール。各取引所が提供する取引履歴をアップロードしていけば、自動で各年の収支をはじき出してくれる。
ただ、実際には未分類取引、すなわちエラーが発生する場合があり、正確に計算するためには、そうした未分類取引をなるべく解消しなければならない。
以下に、使っていて気付いた留意点を書きとめておく。新たな発見があれば時々更新したい。
全体として押さえておくべきこと
・時刻の情報を正確に
計算にあたって、すべてのデータを時系列に並べている。積立やチップで得たコインの取得日時をも正確に記述する必要がある。順番がずれると、コインが足りないとされて未分類取引になってしまう。
また、高頻度系botを回している場合、同じ秒に別種の約定が発生する場合がある。このとき、近くの別の約定もまとめられてしまう場合がある。順番に通貨ペアを渡り歩くような発注で、未分類取引にされてしまう場合は、1秒程度各約定時刻を離して記述しなおすとうまく通ることがある。
・データを分割して保管
データのデバッグにあたって、年をまたいでいるデータファイルがある場合、1年ごと、あるいは数か月単位で区切って保管しておくと、どこから未分類取引が生じたのかがわかりやすくなる。アップロードしていくときは、古いものから順に追記していくとよい。
Zaif
・コイン積立
2018/11/24現在、cryptactはZaifのコイン積立に対応していないので、カスタムファイルを作って補完しなければならない。カスタムファイルのフォーマットはcryptactのサポートページから手に入る。またコイン積立の履歴はZaifのユーザーページから見ることができるが、csvなどでダウンロードできるわけではないので、手でデータを採取してくるしかない。コイン積立の履歴ページから、excel等にコピペして整形するとよい。
・簡単売買
BitFlyerの販売所などの場合、販売所の売買履歴もcsvとして手に入る。しかし、zaifの簡単売買は、簡単売買のページに飛んでそこで履歴を手動で取得してこないといけない。取得した履歴をカスタムファイルに追記すると反映される。
・Twitterチップ
チャットチップの送受信履歴はcsvでダウンロードできるが、Twitterチップの履歴はそうではないらしい。ソーシャル設定→プロフィールと進んで履歴を取得しないといけない。しかも最新の履歴数件しか表示されていない。Twitter受取チップ総額は表示されているので、一応総額は把握できるが、正確な受取時刻がわからない場合が出てくる。
・残額調整履歴
ハッキングを受けたMONAやBCHの返却が11月末ごろから始まった。場合によってはハッキング時の保有コイン数の数割を日本円で返金している模様で、その履歴は残額調整履歴のところに載っている。この分は、コインを売った形とみなして、カスタムデータに記述しておかないと未分類にされてしまう。
・信用売買
現物のBTCの板をそのまま利用した信用取引を行うことができる。そこでは、JPY以外にもBTCやXEMといった仮想通貨を証拠金として利用することができる。ポジションを決裁したときには証拠金として預けていた仮想通貨が返金される。もし損失が出たら、まずはJPYから充当されるが、それで足りない場合は証拠金の仮想通貨から補填する。このあたりの処理で、現物の残高がマイナスになると言われる場合がある。これは、複数のポジションを保有しているときに、エントリーの順番と決済の順番が入れ替わると発生する可能性がある。空売りを現物売りと同様に処理しているため現物が実態以上に減っていることになる。仕方がないので、カスタムファイルで、エラーが出る注文群の先頭で現物を補充し、全ポジション決済後にその現物を同値で売ったことにして対処する。
・csvデータの更新
zaifでは毎日夜にcsvデータが更新される。更新時の1日前の取引までが記載される模様。
↑ 5日に1回くらいしか更新されてないみたい…?
1日1回更新申請が行えるので、前日までの取引は手に入れられる模様(2019/2/16 追記)
BitFlyer
・Lightning 取引履歴
Lightningの履歴を普通にダウンロードしてくると、最近の65535件しか記載されていない。仕方がないのでデータ取得の日付範囲を指定して取得する。高頻度系botをやっている人は特に大変だと思う。
データシートにはParent Order, Child Order, Executionsの3種記載されているが、実際に計算の際見ているのはExecutionsの模様。もしノーポジのはずなのにBTC-FXの所有枚数が0になっていない場合は、Executionsの記載の過不足を確認するとよさそう。
取引履歴(4種)を一括でダウンロードできるようになったので、上記問題は解消。
bitbank
・取引履歴
デフォルトの取引履歴は直近48時間分から最大500件まで取得可能。それ以前の履歴は "2日(48時間)以前のデータはこちらから検索できます" というところから同様のページに飛べるが、ここでも500件ごとにしか取得できない。API叩いても500件しか取得できない。高頻度系botをやっている人はめちゃくちゃ大変だと思う。
サポートに問い合わせてみたら、1週間くらいで送ってくれるとのことである。
→1週間後くらいに、全取引履歴csvへのリンクを送ってくれた。個別対応するほうがむこうとしてはコストがかからないんでしょうか…
2019年verの取引履歴を出力してもらうときは 30 分程度で送付してくれた。
Liquid
・ゼロ数量売買はSelf-matchをチェック
現物取引でゼロ数量売買の未分類エラーが出る場合がある。その場合は、データをチェックし、売買形態がSelf-matchになっている取引を見つける。Self-matchのうち、手数料が発生せず、かつJPYが絡んでいない取引の場合、エラーを返す模様。そうした取引を削除する。
・有効桁数の問題
Liquid は他の取引所に比べ各コインの表示桁数が多いので、端数が積もってズレが生じていそう…。
海外未対応取引所
・時刻のタイムゾーンに注意
デフォルトで対応している取引所であれば、タイムゾーンの処理は自動で行ってくれる。しかし、まだ対応していない取引所での取引を記録する際には、カスタムファイルを用いる必要がある。ここで、海外取引所では、UTCで時刻を表記している可能性があるので、おそらく日本標準時に合わせたほうがうまくいく。
カスタムファイル
・数式を利用した excel シートはたぶんアップできない
bitbank, Liquid
(excel の csv 出力のデフォルト文字コードは Shift-JIS だが、これらの取引所から出力される csv の文字コードは UTF-8 であるため。また、UTF-8 で csv 出力したとしても、最後尾の列に意図せぬ空列が入り込み、エラーが起きる場合がある。このときは VS Code をはじめとするテキストエディタで編集するとよい)
【Python】浮動小数点処理とdecimalモジュール
・はじめに
cryptocurrency bot制作でしばらく悩まされていた不具合の原因が小数計算にあることに気づき、Pythonにおける浮動小数点処理を調べる必要に迫られた。decimalモジュールを利用する際の備忘録として以下にまとめる。付け焼刃なので間違っているかも。
・とりあえず使うために
まずはインポート
from decimal import *
Decimal()の引数に数値か文字列を与える。数値で与えるとfloatと同じ。文字列で与えると10進数同様の取り扱いができる。こうすれば四則演算は普通に行ってよい。
a=Decimal(0.1) #floatとおなじ b=Decimal('0.1') #10進数扱い c=0.1 #float d=Decimal(str(c)) #10進数扱い
丸め処理は.quantize()を用いる。roundingを指定することでいくつかの丸め処理が可能。とりあえずゼロ丸めだけしたかったのでその例のみ記す。quantizeの第1パラメータには揃えたい桁数を有する値をDecimal形式で与える。1E-4などで与えてもよい。第2パラメータにはroundingを指定する。指定しなければDecimalのデフォルトの丸め形式が実行される。
Decimal('0.01283').quantize(Decimal('0.01'),rounding=ROUND_DOWN) #0.01 Decimal('0.314155').quantize(Decimal('1E-2'),rounding=ROUND_DOWN) #0.31
一つの丸め処理しかしない場合はデフォルトの丸め形式を指定しておくと楽そう。またデフォルトの有効桁数を指定することもできるようである。
getcontext().prec=24 #24桁にする print(Decimal('0.00000038471942')/Decimal('0.0000312413')) #0.0123144497828195369590894 getcontext().rounding=ROUND_DOWN #切り捨てに設定 getcontext().prec=10 #10桁にする print(Decimal('0.00000038471942')/Decimal('0.0000312413')) #0.01231444978
なお、decimalで得た数値を他の関数に与えるなどの場合はintやfloatにキャストしてあげないといけない。
・速度を調べる
floatで計算したときより速度が遅いらしいので、雑に速度を比較した。以下のようにひたすら小数を足し上げるのにかかる時間を測定。
import time from decimal import * start=time.time() sum1=Decimal('0') for i in range(1000): sum1+=Decimal('0.1111') end=time.time() print(sum1,end-start) start2=time.time() sum2=0 for i in range(1000): sum2+=0.1111 end2=time.time() print(sum2,end2-start2)
値と実行時間の結果は以下の通り。
float合計 | decimal合計 | float実行時間 | decimal実行時間 | |
100回 | 11.110000000000026 | 11.1100 | 0.0009992123 | 0.0009994507 |
1000回 | 111.0999999999974 | 111.1000 | 0.0010001659 | 0.0009992123 |
10000回 | 1110.9999999999386 | 1111.0000 | 0.0019972324 | 0.0049958229 |
100000回 | 11110.00000000701 | 11110.0000 | 0.0129878521 | 0.0489549637 |
1000000回 | 111099.99999810797 | 111100.0000 | 0.1228837967 | 0.4615705013 |
10000000回 | 1110999.9999210974 | 1111000.0000 | 1.1908910275 | 4.4668421745 |
単純な足し算だからか、実行時間はほぼ線形に増加した。floatで扱ったときとdecimalで扱った時では約4倍の開きがあった。計算がより複雑になると変わってくるかもしれないが、まあメチャクチャ遅いって感じはしない。
仮想通貨取引所用ライブラリ ccxt を導入する
仮想通貨取引botを細々といじるにあたり、今までは取引所ごとに異なるライブラリを用いていた(e.g. pybitflyer, zaifapi)が、海外取引所などもまとめて一つのライブラリccxtで扱えるらしいので、それぞれの環境に導入した。
・at AWS Cloud9
ターミナルで $sudo pip-3.6 install ccxt とすればモリモリインストールされる。
・at jupyter notebook
Anaconda prompt で $pip install ccxt とすればモリモリインストールされる。あるいは白紙のnotebookで !pip install ccxt としても可能。
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools"
って怒られたので、visual studio build tools 2017 を拾ってきてから再度試したらモリモリインストールが完了した。