エンジニアの頭の中

トレードbotエンジニアが書く技術と投資のブログ

【BitMEX】REST APIでPost-Only注文を行う方法

f:id:mitsu3204:20180910024229j:plain

BitMEXで発注時にTakerになって手数料を徴収されることを防ぐため、APIからPost-Onlyオプションを指定して注文を実行する方法について説明します。

目次
  • BitMEXのMaker手数料とTaker手数料
    • Maker手数料
    • Taker手数料
    • 具体例
    • 意図せずTakerになってしまう場合がある
  • Takerになることを防ぐためのPost-Only
    • Web画面のPost-Onlyオプション指定方法
    • APIのPost-Onlyオプション指定方法

BitMEXのMaker手数料とTaker手数料

BitMEXは約定時に手数料が発生しますが、Maker手数料とTaker手数料というものがあります。

Post-Onlyを使う動機は、Maker手数料を手に入れること&Taker手数料の支払いを避けることだと思うので、まずMaker手数料とTaker手数料について説明します。

Maker手数料

Makerとは、板(オーダーブック)に注文を並べる人のことを指します。発注後にすぐに約定せずに板の注文価格と量が表示されているのがMakerです。

f:id:mitsu3204:20180906185228p:plain

指値で注文を行う場合、通常は注文金額と量が板に乗っかりその注文はMakerとして扱われます。

このMakerとしての注文が約定すると、BitMEXではMaker手数料として-0.025%が発生するのですが、手数料率がマイナスなので取引額の0.025%分を頂くことができます

例えば、10,000 USD分のビットコインをMaker注文で購入した場合であれば、Maker手数料として10,000 USDの0.025%にあたる2.5 USD相当のビットコインがもらえるというわけです。

Makerが、手数料を支払うのではなく貰える理由は、Makerが市場に流動性を提供しているからです。Makerの注文があることによって、市場の参加者は購入や売却ができるため、Makerは価値を提供しているわけです。その価値に対する報酬としてMaker手数料がもらえるという仕組みです。

Taker手数料

Takerとは、発注後に板に乗らずに即座に約定する注文のことです。TakerはMakerの出した板上の注文を消化します。

成行注文や、既に板に並んでいる価格を指定した指値注文が、Takerに該当します。

Takerの場合、約定した際には取引額の0.075%がTaker手数料として徴収されます(支払う)。

Takerは、Makerの提供した流動性を享受しているため、その価値に対して手数料を支払っているというわけです。

具体例

具体的なMaker、Takerの例として、板の状態と指値の価格を見てみましょう。

f:id:mitsu3204:20180906185228p:plain

上記の板の場合、best bid と best askが6358.0 USDと6358.5 USDなので、買い注文、売り注文の際の指値とMaker、Takerの関係は以下の表の通りとなります。

- 6358.0 以下 6358.5 以上
買い注文 Maker Taker
売り注文 Taker Maker
意図せずTakerになってしまう場合がある

相場は常に動いているため、一瞬で板の状況が変わる可能性があります。

Makerを狙って指値で注文した場合でも、次の瞬間には自分が指定した指値の価格に対応する反対方向の注文(買い、売りのこと)が発生していて、注文と同時にすぐに約定して、意図せずTakerとなってしまう場合があります

例えば先ほどの板の場合であれば、Makerになるつもりで6358.0の価格で買い注文を発行したけど、注文が受け付けられるまでの一瞬や数秒の間に、板の状況が変わり6358.0が最も安い売り注文として板に並んでしまった場合です。

この場合、Maker手数料を手に入れるつもりで出した指値注文が、Takerになったことによって逆に手数料を徴収されてしまうということになります。

ある程度の大きさの値幅を狙って売買をしている場合、Taker手数料を徴収されても大して気にすることはないと思いますが、小さな値幅を狙って高頻度で取引している場合や、Maker手数料をもらうこと自体を目的とした取引の場合は、この不意にTakerになってしまうことが無視できない痛手になる可能性があります。

Takerになることを防ぐためのPost-Only

発注時にPost-Onlyオプションを指定することによって、上記のように意図せずTakerになってしまうことを防ぐことができます。

このオプションを指定して注文を実行すると、Takerになってしまう場合は、自動で注文がキャンセルされるのです。

Web画面のPost-Onlyオプション指定方法

Web画面上でPost-Onlyオプションを指定して注文を行うには、以下の赤枠で囲ったチェックボックスをチェック状態にすることで、該当のオプションを指定することができます。

f:id:mitsu3204:20180906181413p:plain

APIのPost-Onlyオプション指定方法

APIから注文を行う際にPost-Onlyオプションを指定するには、以下のパラメータを指定することによって、Post-Onlyオプションが有効になります。

パラメータ名
execInst ParticipateDoNotInitiate

BitMEXのAPIの公式ドキュメント(英語)には記載されています。

f:id:mitsu3204:20180906193017p:plain

ソースコードは、Pythonでccxtを使っていれば以下のようになります。

import ccxt

bitmex = ccxt.bitmex({
    'apiKey': 'APIキー',
    'secret': 'APIシークレット'
})

bitmex.create_order(symbol='BTC/USD',
             type='limit',
             side='<Buy または Sell>',
             amount= <注文量>,
             price = <注文単価>,
             params = {'execInst': 'ParticipateDoNotInitiate'})

orderメソッドの引数であるparamsに、辞書型でパラメータを指定できるので、上記のパラメータを文字列で指定するだけです。

気をつけるべき点としては、Post-Onlyオプションを指定して注文を行った場合、Takerとなる注文は自動でキャンセルされるので、自動キャンセルされた場合の対応を考慮しておくことが重要です

注文がキャンセルされたままで良いのか、それとも指値を変更して注文を出し直すべきなのかなど、開発する側の要件に応じて対応は異なってくると思います。

以上になります。

ccxtの使い方を知りたい場合は、以下の記事をご覧ください。

www.hacky.xyz

BitMEXのWebSocket APIを使ったbotの開発についても記事を書いています。興味があるかたは、ぜひご覧ください。

www.hacky.xyz

iPhoneやiPadでPythonプログラミングができる!iOSアプリPythonista 3を紹介

f:id:mitsu3204:20180904164222j:plain

iPhoneiPadPythonプログラミングができるアプリPythonista 3を紹介します。Python好きな人、手軽にプログラミング入門したい人、iOSアプリに興味があるPython使いな人にオススメです。

目次

  • Pythonista 3とは
  • Pythonista 3の特徴
  • まとめ

Pythonista 3とは

Pythonista 3 - omz:softwareは、PythonプログラミングをするためのiOSアプリです。

Pythonista 3を使えば、iPhoneiPad上でPythonプログラミングができてしまうんです。しかも、Objective-CやSwiftでコードを書かなくても、PythoniOSアプリを作ることができてしまいます。

Pythonista 3は有料アプリで、価格は¥1,200なんですが買い切りですし、PythoniOS開発環境がこの価格で手に入るなら安いと思い即買いしてしまいました。

アプリはApp Storeからダウンロードできます。

Pythonista 3の特徴

Pythonista 3の特徴をいくつか紹介します。

コーディングしやすいエディタ

iPhoneiPadを使ってタップしてコードを書くとなると、ちょっとやりにくそうに感じてしまいます。

ですが試してみたところ、Pythonista 3のキーボードは良くできていて、コードの最初の断片を入力すれば、その続きを予測して候補を表示してくれるので、ほとんどのコードは候補から選択していくだけで、以外にも簡単にコーディングできてしまうのです。

iPhoneを使って片手でコーディングできるなんて時代は変わったなと感激しました。

f:id:mitsu3204:20180904171822j:plain

もちろん、キーボードをBluetoothで接続すれば、キーボードでコードを書くことも可能です。 特にiPadの場合、カバーを装着すればiPadを横向きにして置くことができるので、その状態でキーボードのセットで使うと、コーディングがかなり捗ります。

f:id:mitsu3204:20180907115021j:plain

豊富なサンプルアプリケーション

Pythonistaには、いくつかサンプルコードが保管されていて、実際にそれら動かしてみることができます。

アニメーション、ゲーム、ユーザーインターフェースなど複数のカテゴリがあり、カテゴリごとに複数のサンプルアプリケーションがあります。

懐かしいブロック崩しゲームや、アナログ時計、電卓などのツールもありますし、グラフ描画のアプリなどもあり、サンプルアプリケーションを動かしてみるだけでも楽しいです。

f:id:mitsu3204:20180904165829j:plain f:id:mitsu3204:20180904165917j:plain f:id:mitsu3204:20180904165955j:plain

主要なライブラリは最初から使える状態になっている

requests、bs4、numpy、matplotlibなどメジャーなPythonライブラリは最初からインストールされているので、環境準備などせずに、アプリをインストールしたらすぐにいつも通りのプログラミングを始めることができます。

2系と3系の両方のバージョンのPythonが使える

Pythonの最新バージョンは3系ですが、まだまだ2系も多く使われていますよね。

Pythonistaは、設定で使用するPythonのバージョンを2.7か3.6のどちらかを選択できるのです。

書きたいバージョンをサクッと選べるのは嬉しいですね。

外観のテーマを選べる

設定で外観のテーマを変更することによって、アプリの見た目が変えられます。テーマは全部で10種類あります。

f:id:mitsu3204:20180904170800j:plain

ちなみに、Pythonista 3アプリ自体のアイコンも変更できます。

f:id:mitsu3204:20180904170816j:plain

まとめ

Pythonista 3とっても良いですね。Pythonさえ書ければ、タップだけでサクッとツールなんかも作れてしまいとっても便利です。

特にパソコンが使えない状況でもiPhoneiPadを使ってコードを書きたい人や、プログラミングに興味はあるけどパソコンを持っていない。iPhoneiPadなら持っているなんて人にはオススメです(パソコンを買うお金がない学生とか良いかも)。

アプリがプログラミングを始めるきっかけになれば良いですね。

興味を持ったかたは、ぜひアプリをインストールしてみてください。

【プログラミング未経験者向け】なぜPythonがプログラミング入門に適しているのか?

f:id:mitsu3204:20180827211329j:plain

目次

  • プログラミングの入門に適した言語はなにか?
  • Pythonがプログラミングの入門にオススメな理由
  • Pythonをさわってみる

プログラミングの入門に適した言語はなにか?

プログラミング言語はたくさん存在する

世の中にはたくさんのプログラミング言語があります。

C、C++JavaRubyJavaScriptPythonHaskellScalaなど書き出すとキリがありませんが、これからプログラミングを始めようとする人は、まずどの言語を学べばよいのか迷ってしまうと思います。

最初に学ぶ言語によって学習効率は異なる

プログラミング言語には、その性質の違いから初心者にとっての学びやすさに違いがあります。

難しい言語から手をつけてしまうと、なかなか習得が進まず途中でやる気を無くしてしまうかもしれません。そうならないためには、最初の学習のハードルは低いに越した事はありません。

順序よく学んでいくこと、きちんと理解していくこと、また、自分が学んだことについて、その成果を感じ取れることが、学習を継続するために重要になってくると考えています。

入門に適したプログラミング言語を選択することによって、最初の学習の難易度を下げ、より効率良く挫折しにい状態で学んでいくことができるのです

入門に適したプログラミング言語 Python

私がプログラミングを始めて学ぶ人にオススメする言語は、Pythonという言語です。

Pythonは、シンプルなコード、豊富なライブラリ、汎用性の高さなどが特徴のプログラミング言語で、最近では、ビッグデータの処理やAIの機械学習、Webシステムなど、様々な分野で使われています。

Pythonであれば、スムーズな学習ができ、なおかつ学んだスキルの活躍の場も広く用意されているでしょう。

次は、具体的なPythonの特徴となぜPythonがプログラミングの入門言語に適しているのかを説明していこうと思います。

Pythonがプログラミングの入門にオススメな理由

f:id:mitsu3204:20180903144143j:plain

Pythonは少ないコードで多くを実現できる

Pythonソースコードは、他のプログラミング言語よりもシンプルに書くことができます

Pythonソースコードのシンプルさは、プログラミングの最初の一歩のハードルを低くするもので、プログラミング入門者にとってPythonは易しいでしょう。

具体的にどのようにシンプルなのかというと、書かなくてはならないソースコードの量が少ないこと、書き方が限られていることなどが挙げられます。

ある機能を実現しようとした際に、少ないソースコードで書けるというのは、最初に覚えなくてはならないことが少なくて済みますし、ソースコードの書き間違いも起きにくいです。

また、同じ事を実現するために多様な書き方ができてしまう言語は柔軟ではあるものの、プログラミング初心者にとっては迷いや混乱にも繋がります。

とはいえ、Pythonは初心者専用のプログラミング言語というわけではありません

プログラミング上級者になってもPythonは使いますし、他のプログラミング言語と同様で奥が深いです。Pythonという言語が他のプログラミング言語と比べて人を選ばないというだけです。

Pythonは汎用性の高い言語

Pythonは、何か決まったものを作るための特化した言語ではなく、Web、ゲーム、データ解析、GUIアプリなど、なんでも作ることができる非常に汎用性の高い言語です。

特化した言語と違い、汎用性の高いプログラミング言語は、作るシステムの性質が異なっても、新たに別の言語を覚える必要がありません。学習済みの技術を使ってプログラミングをすることができるのです。

Pythonは電池入り

Pythonは「Battery Included(電池入り)」と言われています。

Pythonには、プログラミングするうえで必要となる便利なライブラリ(プログラムから利用する機能の集合のこと)や開発環境が、Pythonには最初から付属しているのです。

後からあれこれ付け足すことなく、最初からすぐに使える状態になっているので、「Battery Included」と言われています。

また、Pythonには非常に多くの優れたサードパーティのライブラリも存在します。

これらの充実したライブラリによって、Pythonプログラマはすぐにでも本格的なプログラミングを始めることができますし、プログラミングしていて欲しい機能がでてきた場合でも、汎用的な機能であればライブラリが既に存在していて、自分で開発せずにそれらのライブラリを活用すれば済むということが多いです。

Pythonは今最も人気のある言語

Pythonは年々プログラマの人気が上がっている言語で、「The 2018 Top Programming Languages - IEEE Spectrum Rankin」の人気のある言語ランキングで、2017年に続いてPythonが2年連続一位となりました。

人気のあるプログラミング言語を選択することは重要なことです。

人気があるということは、それだけ世の中にそのプログラミング言語の需要があるということであり、修得者の多いプログラミング言語であれば、学ぶ際にも書籍やブログ記事、プログラミングスクールなども多く存在していて学びやすいものです。

Pythonの活用シーンが増えてきている

なぜ、Pythonの人気が高まっているのでしょうか?理由は、いくつかあると考えられています。

最近では、ビッグデータ処理、統計、機械学習、AIなどの分野が盛んですが、Pythonにはこれらの分野に適したライブラリが充実していることもあって、Pythonが利用されることが多くなってきています。

また、Pythonの言語自体の扱いやすさ、マシン自体の性能向上による実行時のパフォーマンスの向上なども理由となって(※)Pythonが選択されることが増えているようです。 ※Pythonのようなスクリプト言語は、コンパイル言語と比較すると実行時の速度は遅いので

また、以前からGoogle社でもPythonが積極的に使われているのは有名な話ですね。

Pythonエンジニアの年収は高い

プログラミングを習得して、プログラマ(=エンジニア)として仕事をして収入を得ようと考えているのであれば、得られる年収がどの程度のものなのかは、気になるところだと思います。

プログラマの年収はピンキリでで、当然ですが、個人の能力や会社や国などによって違います。。

ただ、言語別の年収ランキングを見ると、Pythonエンジニアはランキング上位に位置するようです。

プログラミング言語の違いによる年収への影響

ビズリーチが調査した「プログラミング言語別年収中央値を発表、求人検索エンジン「スタンバイ」調べ」」によると、プログラミング言語別の年収ランキングのベスト10のうち、上位3位は以下のような結果でした。

  1. Go:中央値600万円、最大1600万円
  2. Scala:中央値600万円、最大値1300万円
  3. Python:中央値575.1万円、最大値1499万円

中央値で、GoとScalaには及ばないものの、Pythonは3位と高いところに位置しています。

Pythonをさわってみる

f:id:mitsu3204:20180829203427j:plain

実際にPythonのコードを書いて動かしてみましょう。

Pythonをインストールする

Pythonには、大きく分けて2系と3系のバージョンがありますが、試す程度であればどちらを使っても構いません。

使っているパソコンがMacの場合、MacはOSがPythonを利用しているため、Pythonの2系が既にインストールされておりインストール作業は不要です。

Windowsの人はPythonのインストールを行いましょう。インストール手順はネット上のいろんな記事で紹介されていますが、こちらがわかりやすいと思います。(こちらは3系のインストール手順です)

本格的に学び始めるなら、利用が拡大していく新しいバージョンを使ったほうが良いと思います。Macで3系のインストールをしたいかたは、こちらの記事などみて試してみてください。

はじめてのプログラムを書いてみる

プログラミングの世界では、初めてその言語でプログラムを作成してみる際には、慣例としてまず最初に「Hello world」の文字列を表示してみることが多いです。

その「Hello world」を出力するためのコードを書いて見ましょう。

Pythonを起動する

通常プログラミングというと、ソースコードを書いてファイルに保存して、そのファイルを使ってプログラムを実行するという流れが一般的ですが、Pythonには、対話的にコードを実行できるインタラィテクブシェルというツールが備わっています。

これによって、簡単にPythonのコードを書いて実行することができるので試してみます。

インタラクティブシェルを起動するために、Macの場合は、「terminal(ターミナル)」というアプリ、Windowsの場合は、「コマンドプロンプト」というアプリを起動してください。

f:id:mitsu3204:20180829204520p:plain

terminal(またはコマンドプロンプト)を起動すると、ウインドウが立ち上がり、ユーザーの入力待ち状態となります。(以下はMacのTerminalの場合の例)

Last login: on Sep  3 14:19:24 on ttys001
mymachine:~ hakushim$

$の後ろから文字を入力できます。ここで入力できる文字はコマンドというもので、マシンに対して文字で命令を与えるものです。

ここに、キーボードでpythonと入力してEnterキーを押下します。(Pythonバージョン3系をインストールした場合は、python3と入力します)

$ python

すると、Pythonインタラクティブシェルが起動して以下のように表示されます。

$ python
Python 3.6.2 (default, Jul 30 2017, 16:04:21) 
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
コードを書いて実行してみる

>>>の後ろに以下のようにprint("Hello world")というPythonソースコードを入力します。

>>> print("Hello world")

入力したらEnterキーを押下します。

そうすると、書いたソースコードが実行されて以下のようにHello worldの文字列が表示されます。

>>> print("Hello world")
Hello world
>>>

printというのは、Pythonに組み込まれている関数というもので、このprint関数に文字列を渡すと、その文字列をそのままターミナルへ表示してくれます。

Hello world」の部分を他の文字列に変えれば、その内容に応じて、実行時の表示内容も変化します。

他の言語で書いたHello world

Pythonはシンプルで少ないコード量で多くのことを実現できると先に述べましたが、他のプログラミング言語とどれくらい異なるのか、上記のHello worldを表示するプログラムを例として、比較してみました。

Java言語、Go言語、Python言語の3つの言語でそれぞれHello worldを表示するためのコードは以下のようになります。

Java

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}

Go

package main

import (
    "fmt"
)

func main() {
    fmt.Print("Hello world")
}

Python

print("Hello world")

見ての通り、Pythonが最も少ないコードで簡単に書くことができます。たったの1行で「Hello world」を表示することができてしまうのです。

ただし、勘違いしないで頂きたいのは、この書き方の違いは決してPythonが優れていて他の言語が劣っているというわけではありません

コード量の違いは、それぞれの言語の性質の違いによるもので、他の言語でコードの量が多い理由(書いてあるコードの意味)は、プログラミングを学んでいけば、自然と知ることになると思います。

ここで言いたいのは、あくまでもプログラミングの初心者が最初に学ぶプログラミング言語という観点では、Pythonは覚えなければならないことが少ないので覚えやすいであるということです。

プログラミングを続けていればいつかは、最初に習得した言語以外にも習得しなくてはならない時や、興味が湧いてくる時がくると思います。

他の言語も学ぶことによって、言語の違いを知り、適切な使い分けができるようになり、プログラマとしての能力の底力が上がります。

どうやって学んだらよいのか?

Pythonでプログラミングの学習方法を始めるとしたら、その方法にはどのような選択肢があるのでしょうか?

世の中のプログラマ達の学習方法は様々です。専門学校や大学で学んだ人もいれば、独学で学んだ人もいますし、中には就職後に数ヶ月間の新人研修でプログラミングを学び、その後実際の開発現場へデビューしたという人もいました。

書籍やネットを活用すれば独学でも十分に習得可能です。

特に最近では、オンラインでプログラミング講座が充実しており、これらを利用してプログラミングを学ぶ人が増えているようです。

学習方法についていくつか紹介しておきます。

書籍

Pythonの入門者向けの書籍には、様々なものが出版されています。

Amazonなどで検索すると、Pythonを使った機械学習の入門書も多く出てきますが、いきなり専門分野に飛び込むのではなく、まずはPythonによるプログラミング自体を学ぶ必要があるため、Pythonそのものの入門書を読むと良いと思います。

読むならPythonの3系のバージョンに対応した書籍を選択すると良いでしょう。とはいっても、最近の書籍は、どれもPythonバージョン3系に対応しているはずです。

ちなみに、私はPythonを覚え始めたころは、「Pythonチュートリアル」や「 Python ポケットリファレンス」、「Head First Python 第2版 頭とからだで覚えるpythonの基本」あたりを読みました。

無料サイト

ネットには、Python初心者向けにインストールの方法や、プログラミングの初歩について書いてくれているサイトが複数あります。

お金がかからないのがメリットです。

PythonWebでは、変数、分岐、繰り返しなどプログラミングの基礎中の基礎の部分であれば十分学べるようです。

まずはこういったサイトを見て試してみて、本格的に学ぶ気になったら、書籍などを購入すれば良いと思います。

Udemy

f:id:mitsu3204:20180903182048p:plain

Udemy は、オンライン上で動画で学習するためのサイトです。動画を見ながらプログラミングや英語、数学など様々なものを学習することができます。

UdemyにはPythonを学習するための様々な講座があり、入門者向けにはPython3の入門オンライン講座 という講座がありますので、書籍より動画のほうが学習しやすいという人は、こういったものを利用してみるのも良いでしょう。

Codeacademy

f:id:mitsu3204:20180903182317p:plain

Codecademyは、オンライン上でプログラミングを学べるサービスで、教材に沿って進めていくのですが、Webブラウザ上でコードを書いていくのが特徴です。また、サイトは英語表記なので、英語が苦手なかたには敷居が高いと思われます。各コースの受講は有料となります。

CodeCamp

f:id:mitsu3204:20180903182833j:plain

プログラミングのオンラインスクールのCodeCamp は、現役のエンジニアにマンツーマンで指導してもらえるオンラインのプログラミングスクールです。

単なる動画ではなく、講師付きなので学習の質は高いですが、それなりにお値段も高いので、お金をかけられる人向けです。

まとめ

人によって、学習のために使える時間やお金は異なるので、最適な学習方法は人それぞれ異なってくると思います。

何にせよ、肝心なのは手段ではなくやってみることだと思います。

どんな手段でも良いのでまずは始めてみれば、最適な学習方法も見えてくるのかもしれません。

HTMLからタグを抽出!BeautifulSoupによるWebスクレイピング入門【Python】

f:id:mitsu3204:20180824013430j:plain

目次

WebスクレイピングとBeautifulSoup

Webスクレイピングとは、WebサイトのHTMLを解析して、ページに記載されているタグを抽出することです。

詳しくは、Wikipediaを見てもらったほうが良いかと思います。

ウェブスクレイピング - Wikipedia

Webスクレイピングでできること

Webスクレイピングの用途としては、Webサイトの情報を定期的に自動収集するスパイダー的なものや、Webサイトの操作を自動化して業務の効率化を図ったり、ページのテストに使ったりします。

import.ioのように、Webスクレイピングをサービスとして提供している企業もありますね。

ただ、Webスクレイピングによって情報を収集することを禁止しているWebサイトもあるので、自分でWebスクレイピングを行う場合は注意しましょう。

BeautifulSoupはWebスクレイピングのためのPythonライブラリ

BeautifulSoupとは、HTMLやXMLを解析してタグの抽出を行うためのPythonライブラリです。

Beautiful Soup: We called him Tortoise because he taught us.

PythonでWebスクレイピングをする場合は、まず間違いなくBeautifulSoupを使うでしょう。シンプルに扱うことができて、非常に便利なライブラリです。

BeautifulSoupの使うための準備

BeautifulSoupをインストールする

pipを使って「BeautifulSoup 4」というライブラリをインストールします。名前はbeautifulsoup4となっています。

$ pip install beautifulsoup4

※お使いの環境によってはpipではなくpip3の場合もあります。

BeautifulSoupの現在のバージョンは、4.6系です。

requestsをインストールする

もう一つライブラリをインストールします。「requests」といって、HTTP通信を行うためのライブラリです。

Pythonには、urllibというモジュールがあり、このモジュールを使ってHTTP通信を行うコードを書くことは可能なのですが、requestsを使うと、urllibより簡単にコードを書くことができます。

HTMLのタグスクレイピングに限らず、HTTP通信を伴う様々な処理で活躍するライブラリです。便利なのでインストールしておきましょう。

$ pip install requests

BeautifulSoupの使い方

HTMLページを解析してみましょう。処理の流れとしては、以下のとおりです。

  1. HTTP通信を行ってHTMLを取得
  2. 取得したHTMLを解析してタグを抽出

requestsでWebページを取得する

BeautifulSoupの公式ページにアクセスしてみます。

requestsを使用してWEBページを取得するには、以下のコードを書きます。

import requests

res = requests.get('https://www.crummy.com/software/BeautifulSoup/')

これは、GETメソッドで指定したURLのWEBページを取得しており、戻り値を格納している変数resには、HTTPレスポンスの情報が詰まっています。

HTMLの内容は、res.textに格納されています。以下のようにprint関数でres.textを表示してみましょう。

# レスポンスのHTMLの内容を出力する
print(res.text)

実行結果

<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<title>Beautiful Soup: We called him Tortoise because he taught us.</title>
<link href="mailto:leonardr@segfault.org" rev="made"/>
<link href="/nb/themes/Default/nb.css" rel="stylesheet" type="text/css"/>
<meta content="Beautiful Soup: a library designed for screen-scraping HTML and XML." name="Description"/>
<meta content="Markov Approximation 1.4 (module: leonardr)" name="generator"/>
<meta content="Leonard Richardson" name="author"/>

... 省略 ...

このとおり、requestsを使うと簡単にHTMLを取得することができます。

HTMLのタグ構造を解析する

では、実際にHTMLのタグを抽出してみたいと思います。

先ほどのコードに、BeautifulSoupを使ってHTMLタグを解析するためのコードを書き足します。

import requests
from bs4 import BeautifulSoup

res = requests.get('https://www.crummy.com/software/BeautifulSoup/')
bs = BeautifulSoup(res.text, 'html.parser')

from bs4 import BeautifulSoupによって、bs4モジュールからBeautifulSoupオブジェクトをimportしています。

bs = BeautifulSoup(res.text, 'html.parser’)で、requestsで取得したレスポンスの内容(HTML)を渡して、BeautifulSoupオブジェクトを作成しています。

BeautifulSoupオブジェクトを作成する際に、解析対象がHTMLなのであれば、第二引数でhtml.parserとHTMLのパーサーを指定する必要があります。

指定したタグを取得する

HTML内のタグの内容を取得するにはbs.titleのようにBeautifulSoupオブジェクトに続けて.タグ名と記述します。

print(bs.title)

上記のコードを実行すると、titleタグの内容が出力されます。

実行結果

<title>Beautiful Soup: We called him Tortoise because he taught us.</title>

タグが入れ子構造になっている場合は、取得結果には子タグの内容も含まれます。

bs.headのように書いてheadタグの内容を取得すれば、headタグの入れ子となっているmetaタグやtitleタグなどがまとめて取得できます。

print(bs.head)

実行結果

<head>
<meta charset="utf-8"/>
<title>Python - Wikipedia</title>
<script>document.documentElement.className = document.documentElement.className.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );</script>

... 省略 ...

</head>

bs.xxxxとタグ名を指定した際に、該当のタグ名が解析したHTMLページにtitleタグが存在しない場合は、Noneが返されます。

print(type(bs.title))とtype関数でbs.titleの調べるとわかりますが、このbs.タグ名で取得したオブジェクトは、bs4.element.Tagクラスのオブジェクトです。

タグのボディを抽出する

HTMLタグを解析して、何かしらの処理を行う際には、HTMLタグは除去して中身のボディ部のみが欲しいという場合があります。

ボディ部のみを取得する場合は、bs.title.textように書きます。

print(bs.title.text)

これを実行すると、titleタグの先頭の開始タグと末尾の終了タグに囲まれたボディ部のテキストのみを取得できます。

実行結果

Beautiful Soup: We called him Tortoise because he taught us.

タグの属性を抽出する

タグの属性(class、idなど)を抽出する場合は、Tagオブジェクトからgetメソッドを呼び出します。

bs.meta.get('content')

実行結果

text/html; charset=utf-8

HTML内の元のmetaタグは<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>という内容なのですが、content属性の値が抽出され、text/html; charset=utf-8と表示されました。

タグの属性は、attrsという辞書型のフィールドに格納されています。getメソッドは、このattrsフィールドを参照しているにすぎません。

# 以下の二つのコードは同じことをしている
bs.meta.get('content')
bs.meta.attrs['content']

よって、タグが持つ各属性にまとめてアクセスする必要がある場合は、getメソッドではなくattrsフィールドを使うことができます。

for k, v in bs.meta.attrs.items():
    print(k, v)

複数のタグを抽出する

htmlタグやtitleタグは、一つのHTMLページ内に一つしか存在しないものですが、その他のタグは多くの場合、一つのHTMLページ内に複数個の同じタグが存在するものです。

先ほどのbs.タグ名のような書き方では、複数のタグは対応できず、HTML内に同様の名前のタグが複数存在する場合は、先頭の一つ目が返さるだけです。

複数のタグをまとめて取得するには、以下のようにfind_allメソッドを使います。引数にはタグ名を指定します。

以下は、HTML内に存在する全てのaタグ(アンカー)を取得します。

print(bs.find_all('a'))

実行すると、find_allメソッドの引数に指定したタグの内容をまとめて取得することができます。

実行結果

[<a href="bs4/download/"><h1>Beautiful Soup</h1></a>, <a href="#Download">Download</a>, <a href="bs4/doc/">Documentation</a>, 

... 省略 ...

]

find_allメソッドの戻り値はlistではなく、listを継承したbs4.element.ResultSetのオブジェクトとして返されます。

listと同じように扱えるので、戻り値をそのままループで処理することも可能です。

# 全てのaタグを取得して、href属性があれば、その値(リンク先のURL)を出力する
for a in bs.find_all('a'):
    if 'href' in a.attrs:
        print(a.get('href'))

実行すると、以下のようにaタグのリンク先URL一覧が出力されます。

実行結果

bs4/download/
#Download
bs4/doc/
#HallOfFame
https://code.launchpad.net/beautifulsoup
https://bazaar.launchpad.net/%7Eleonardr/beautifulsoup/bs4/view/head:/NEWS.txt
https://groups.google.com/forum/?fromgroups#!forum/beautifulsoup

... 省略 ...

BeautifulSoupオブジェクトからは、findというメソッドも呼び出し可能です。findメソッドも呼び出し方はfind_allと同様で、find(タグ名)のように使いますが、bs.タグ名のようにHTMlの解析結果から最初の一つ目のタグのみを返すものです。

タグの属性を条件としてタグを抽出する

find_allメソッドでタグを抽出するための条件は、タグ名だけではありません。 タグの属性情報も抽出条件として指定できます。

例えば、HTML内のaタグのうち、content属性の値がtrueのものだけを抽出したいのであれば、find_allメソッドのattrs引数に以下のように辞書形式で、属性の名前と値を指定します。

for meta in bs.find_all(name='meta', attrs={'name': 'author'}):
    print(meta)

※わかりやすさのために引数名を記載しています。記載しなくても動きます。

実行すると、metaタグのうち、name属性がauthorの値のものだけが抽出できます。

実行結果

<meta content="Leonard Richardson" name="author"/>

attrs引数には、辞書形式で複数の属性を指定できるので、さらに厳密に属性を指定して抽出するタグを絞り込むことが可能です。

最後に

PythonとBeautifulSoupを使うと、HTMLを解析してタグを抽出するということが、簡単に実現できるのがおわかり頂けたと思います。

また次の機会にWebスクレイピングについてもっと深掘りした記事も書いてみたいと思います。

最近は、Webスクレイピングに関する書籍も増えたと感じますが、以下の書籍は、オライリー社から出ているもので、Webスクレイピング本に関するオススメ本です。

PythonによるWebスクレイピングをAmazonで見る

Webスクレイピングをネタに一冊本がかけちゃうんです。本の中でもBeautifulSoupを使っていて、Webスクレイピングに興味があるならこれで勉強しておくと良いと思います。普通に面白い本です。

活用している?Python 3.5で追加された型ヒント(Type Hints)

f:id:mitsu3204:20180819161005j:plain

Python 3.5から追加された型ヒント(Type Hints)という機能をご存知でしょうか?もしくは、知っていても活用しているでしょうか?

型ヒントを使用することによって、コーディングがより便利に、かつ保守性を高めたり、不具合リスクを減少させることができます。私は積極的に使用するようにしており、2系のPythonと比べると、型ヒントによって随分とコードを読むのが楽になったと思います。

そんなPythonの型ヒントについて、簡単にまとめてみました。

型ヒント(Type Hints)ってなに?

型ヒント(Type Hints)とは、Pythonソースコード上に、型の情報を加えるものです。

PEP 484 — Type Hints | Python.orgには、以下のコードが示されています。

def greeting(name: str) -> str:
    return 'Hello ' + name

関数greetingの引数であるnameの後ろに: strという記載があります。これによって、この引数nameの型が、strであることを示しています。

また、引数の宣言の後から、:までの間に、-> strという記述がありますが、こちらは、この関数の戻り値に対する型ヒントであり、上記のコードの場合、戻りの型がstrであることを示しています。

このように、ソースコード上の関数の引数、関数の戻り値、変数などに対して、その 戻り値や変数などが何の型であるかの情報を付加する のが、型ヒントということです。

型ヒント(Type Hints)を使う意味

型ヒントによる型の指定は、Pythonプログラムの実行に対して、強制力を持つわけではありません。

例えば、サンプルのgreeting関数のstrの型が指定されたnameに対して、数値型の値を指定することもできてしまいます。

型ヒントを使うメリットには、以下のようなものが考えられます。

静的解析による型チェックが可能になる

静的解析による型の把握が可能になるため、コードを実行しなくても、型のチェックや、コーディングの際に型特有のプロパティの入力補完などが実現可能となる。

例えばIDEの場合、コード上で型が明示されることによって、IDEが型を認識して、該当のクラスのメソッドを入力補完機能で表示できるようになるなどが挙げられます。

/型ヒントが指定されていない場合/ f:id:mitsu3204:20180819161103p:plain

/型ヒントが指定されている場合/ f:id:mitsu3204:20180819161113p:plain

nameがstr型としてヒントが指定されているので、入力補完機能によって、str型のオブジェクトが持つメソッドが、候補としてリストアップされます。これはコーディングが捗りますね。

可読性が上がる

Pythonソースコード上で型が明示されているため、人がソースコードを見た際の可読性が上がります。

型ヒントがなかった時のPythonは、変数や関数を扱う際に、型を確認するのが面倒な場合がありました。

IDEを使っていても、型を把握できるのは限定された状況のみだったので、docを見て型の記載がないと、コードを読む必要がありましたが、宣言部分から一目で判断できる or IDEが自動的に把握可能になったので、かなり楽になったと思います。

型ヒント(Type Hints)の書き方

型ヒントの基本的な書き方です。

関数の引数

関数の宣言部で、引数名の後ろに: 型名と記述することによって、引数の型ヒントを指定します。

def say(word: str):
    print(word)

関数の戻り値

関数の引数の宣言の後から:までの間に、-> 型名と指定します。

def add(a: int, b: int) -> int:
    return a + b

変数

関数の引数と戻り値だけでなく、通常の変数にも型を指定できます。末尾に#コメントで、type: 型名の形式で型を指定します。

a = 'Hello'  # type: str

リスト、辞書、タプル

typingというモジュールにListDictTupleなどが定義されています。コレクションの要素の型まで指定する場合は、これらを使用します。

from typing import List, Dict, Tuple

l = ['a', 'b']      # type: List[str]
d = {'age': 18}     # type: Dict[str, int]
t = (True, False,)  # type: Tuple[bool, bool]

型ヒントによる様々な表現

型ヒントには、いろいろな使いかたがあります。一部を紹介します。

Type alias - 型の別名

型ヒントは、エイリアスを作ることができます。

Name = str

def add_user(name: Name, age: int) -> None: ...

Nameという型ヒントを定義して、add_user関数の引数であるnameに適用しています。

エイリアスを定義する際は、ユーザーの独自クラスを定義する時と同様に、その名称の先頭は大文字とするべきとPEPには記載されています。

エイリアスは、複雑な型を単純化して表現する際に有効のようです。 例えば、以下のようなコードがあったとします。

from typing import List, Dict

def send_message(List[Dict[str, str]]) -> None:
    ....

これは、型エイリアスを使うと以下のように表現することも可能です。

from typing import List, Dict

Address = str
Message = str
To = Dict[Address, Message]

def send_message(to: List[To]) -> None:
    ....

Callable - 呼び出し可能であることを示す型ヒント

呼び出し可能なオブジェクト、つまり関数オブジェクトを期待する場合に、Callableという型ヒントを使用することができます。

from typing import Callable

def call(func: Callable[[str, int], str]) -> None:
    ....

Callableを使用する時は、関数のシグネチャを指定することができます。

上記の例では、関数callの引数であるfuncの型として、第一引数にstr、第二引数にint、戻り値がstrシグネチャを持った関数を指定しています。

NewType - 独自のクラスを定義

型ヒントのための独自のクラスを作るために、NewTypeという関数が用意されています。

from typing import NewType

UserId = NewType('UserId', int)

def func(user_id: UserId) -> str:
    ....

NewType関数によって、UserIdという型を作成しています。 これは、intのサブクラスの位置付けとなるもので、以下の定義と同様です。

class UserId(int):
    pass

UserIdの型ヒントが指定されているところに、intを渡すと、静的検査としては、チェックエラーとなります。

# これはNG
s = func(12345)

# これはOK
s = func(UserId(12345))

ただし、実行時にはなんら制限はありませんので、intを直接渡しても、問題なく動きます。

Generics - ジェネリクス

ジェネリクスも対応しています。Tのように任意の型であることを指定できます。PEPのサンプルコードを見てみましょう。

https://www.python.org/dev/peps/pep-0484/#generics

from typing import Sequence, TypeVar

T = TypeVar('T)

def first(l: Sequence[T]) -> T:
    return l[0]

この場合、関数firstの引数lの要素と戻り値は、同じTという型ヒントを指定しており、一貫性のある型であることを示しています。

つまり、引数lの要素がstr型だとしたら、戻り値もstr型となるし、引数lの要素がint型の場合は、戻り値もint型であるということです。

また、TypeVarは、起こりうる型を指定することができます。

from typing import TypeVar

AnyStr = TypeVar('AnyStr', str, bytes)

def concat(x: AnyStr, y: AnyStr) -> AnyStr:
    return x + y

AnyStrは、strbytesのみを許容するという意味になります。

終わりに

ここで紹介したのが、型ヒントの全てというわけではありません。

私も普段書いていると言っても、基本的な使い方のみで、ドキュメントに記載されている全ての書き方を試したわけではありませんので、もし興味があるかたは、ぜひドキュメントを読んで試してみてください。

Pythonは今でも2系がよく使われているイメージがありますが、バージョンが上がるにつれて、型ヒントのようにどんどん便利な機能も追加されていっているので、積極的に新しいバージョンを採用していきたいですね。

最後に、個人的にPython初級者の学習にオススメの本の紹介です。絵が豊富で、理解と記憶がしやすいように書かれた本です。

Head First Python 第2版 ―頭とからだで覚えるPythonの基本

PythonとBitMEX WebSocket API によるリアルタイムトレードbotの作り方

f:id:mitsu3204:20180819142757j:plain

仮想通貨の自動売買を高速で実現するためには、取引所のデータをリアルタイムに取得する必要がありwebsocketが欠かせません。

本記事では、PythonとBitMEXのWebSocket APIを使用して、仮想通貨のリアルタイムトレードを実現するbotの作り方を記載します。

PythonによるWebSocket APIの実装方法や、websocketを介して受け取るデータの内容に焦点を当てて行きます。

目次

BitMEXとは

BitMEXは自動売買に適した仮想通貨取引所

BitMEXは、ビットコインイーサリアムリップルなど様々な仮想通貨を取り扱う仮想通貨取引所です。

100倍という高レバレッジに対応しているのが特徴です。また、BitMEXはAPIとそのドキュメントが充実しており、自動売買を行うためのボットが作りやすいです。

裁量トレーダーもbotトレーダーも利用者は多いと思われます。本人確認が必要ないので、アカウントをサクッと作りやすいのも良いですね。

www.bitmex.com

BitMEXを使うなら手数料を理解しておくべき

BitMEXを利用するには、BitMEXの手数料体系を理解しておく必要があります。

BitMEXでは取引の際に注文の仕方がTakerかMakerかによって手数料が変わってきます。Takerは、0.075%の手数料が徴収されますが、Makerは-0.025%というマイナス手数料なので、手数料がもらえます。Maker手数料狙いのボットを作るのもアリかもしれないですね。

また、ファンディング手数料というのもあって、1日の中の決まった時間にポジションを保有していると手数料が発生します。ファンディング手数料は、ポジションがロングかショートかによって手数料を支払うのかもらえるのかが異なります。

BitMEXの手数料については、わかりやすく解説してくれている記事があるので、そちらを参考にすると良いと思います。

www.benyasu-btcblog.com

リアルタイム性の高いWebSocket APIがある

名前のとおり、WebSocketを使用したBitMEXのAPIのことです。REST APIを使用してクライアント側から要求を出す方法と異なり、取引に必要な各種情報をリアルタイムに入手することができます。

WebSocketを使用した事が無い人はイメージがつきにくいかもしれませんが、サーバ側の情報に更新があればサーバ側からクライアント側へ、即座にプッシュしてくれるということです。

例えば、発行していた注文に対して約定が発生した場合や、板の情報に変化が発生した場合などに、その変更内容をクライアント側から情報を取りにいかなくてもサーバ側から送ってくれます。

時間にシビアな高頻度の取引を行う場合は、REST APIよりもWebSocket APIのほうが適しているでしょう。逆に大きな時間間隔でトレードする場合は、REST APIのみでも問題無いと思います。

BitMEXでは、REST APIをあまり高頻度に実行していたり、エラーを頻発していると、一時的なアクセス制限を食らったり、該当アカウントによるAPIアクセスを全て遮断される場合があります。

後者の場合は、待っていてもアクセスできるようにはならず、サポートへ問い合わせる必要があるので気をつけてください。

ちなみに、私は証拠金不足エラーを連発した際に、BitMEXのAPIへのアクセスをブロックされたことがあります。ブロックを解除してもらおうとして、サポートに問い合わせたところ、修正したコードをチェックするから提出しろと言われました。

BitMEX公式のサンプルbot(market-maker)

BitMEXのgithubに、Pythonで書かれたmarket-makerのbotサンプルソースがあります。

github.com

このソースコード公式ドキュメントを読めば、大体WebSocket APIの使い方はわかるとは思います。

使用言語とライブラリ

Python 3.6系を使用しました。websocketは、websocket-clientを介して使用しました。 github.com

情報の取得にはWebSocket APIを使用しますが、注文の発行や取り消し等にはREST APIを使用します。

REST APIによる注文の発行や取り消し等にはccxtを使っています。ccxtの使い方については、過去記事をご覧ください。 www.hacky.xyz

WebSocket APIの使用方法

では、BitMEXのWebsocket APIを使用した実装方法を見ていきましょう。

サーバへ接続する

まずは、WebSocketに対応したリアルタイムAPIのURLであるwss://www.bitmex.com/realtimeに接続します。

import websocket

# サーバとのデータのやりとりを表示するため、Trueを指定する。(確認したくないのであればFalseで問題ないです)
websocket.enableTrace(True)

# 接続先URLと各コールバック関数を引数に指定して、WebSocketAppのインスタンスを作成
ws = websocket.WebSocketApp(url='wss://www.bitmex.com/realtime',
                  on_open=on_open,
                  on_message=on_message,
                  on_close=on_close,
                  on_error=on_error)

# BitMEXのサーバへ接続する
ws.run_forever()
コールバック関数を指定する

WebSocketAppを呼び出す際に、urlの他に以下の四つの引数を指定しています。

これらの引数には、関数でサーバからデータを受信した際のコールバック関数を指定します。 わかりやすいように、引数と同名の関数を定義します。

def on_open(ws):
    # サーバとの接続時に実行する処理

def on_message(ws, message):
    # サーバからのメッセージを受信した場合に実行する処理

def on_close(ws):
    # サーバとの切断時に実行する処理

def on_error(ws, error):
    # エラー発生時に実行する処理

各関数が、引数に受け取っているwsというのは、WebSocketAppクラスのインスタンスです。また、on_messageの第二引数である、messageは、受信したメッセージが文字列(str)として渡されます。

接続成功時

サーバへの接続に成功すると、サーバからメッセージを受信し、on_messageが呼び出されます。 on_messageで受け取るメッセージの内容は以下のような、ウェルカムメッセージ的なもので、トレードに必要な情報は入っていないため、無視しても問題ないです。

{
  "info": "Welcome to the BitMEX Realtime API.",
  "version": "2018-08-17T17:20:19.000Z",
  "timestamp": "2018-08-18T03:53:16.727Z",
  "docs": "https://testnet.bitmex.com/app/wsAPI",
  "limit": {
    "remaining": 39
  }
}
  • info: Welcome to the BitMEX Realtime APIというウェルカムメッセージです。
  • version: 2018-08-04T20:30:14.000Zのように日時の情報が入っています。バージョンとのことなので、恐らくサーバ側のアプリケーションの最終更新日時かと思われます。
  • timestamp: 接続時の日時(UTC)です。
  • docs: APIのドキュメントのURLです。
  • limit
    • remaining: 一定時間の間の接続可能な数や購読可能な数には限りがあります。その残りの利用可能な回数を示しています。相当頻繁に接続を繰り返さない限りは、枯渇することはないので、心配はしなくて良いと思います。

トピックを購読する

WebSocket APIで、サーバから情報を受け取るには、受け取りたい情報が配信されるトピックを指定して、購読の要求を行う必要があります。

認証無しで購読可能なトピックには、以下のものがあります。

"announcement",// サイトのお知らせ
"chat",        // Trollbox チャット
"connected",   // 接続ユーザー/ボットの統計
"funding",     // 最新のスワップ資金調達率。 資金調達間隔ごとに送信 (通常8時間)
"instrument",  // 取引高とビッド/アスクなどの最新商品情報
"insurance",   // デイリーの保険基金に関する最新情報
"liquidation", // ブックに記入される清算注文
"orderBookL2", // フルレベル 2 のオーダーブック
"orderBook10", // 従来のフルブックプッシュを使用する上位 10 レベル
"publicNotifications", // システム全体への通知 (短期公開メッセージ用)
"quote",       // ブックの上位レベル
"quoteBin1m",  // 1 分足クォートビン
"quoteBin5m",  // 5 分足クォートビン
"quoteBin1h",  // 1 時間足クォートビン
"quoteBin1d",  // 1 日足クォートビン
"settlement",  // 決済
"trade",       // ライブ取引
"tradeBin1m",  // 1 分足取引ビン
"tradeBin5m",  // 5 分足取引ビン
"tradeBin1h",  // 1 時間足取引ビン
"tradeBin1d",  // 1 日足取引ビン
トピックの購読要求を行う

例えば、5分足のローソク足データを受信したい場合は、以下のように、tradeBin5mのトピックに対する購読の要求を行います。

# 1分足のデータを要求するために送信するデータ
channels = {
    'op': 'subscribe',
    'args': [
        'tradeBin5m'
    ]
}

# サーバにトピックの購読要求を送信する
ws.send(json.dumps(channels))

購読要求を送信するタイミングは、サーバへの接続が完了しているのであれば、いつでも良いですが、起動時に常に必要になるトピックについては、on_openの処理の中で購読要求を送って良いと思います。

購読要求に対する応答を受け取る

トピックの購読要求を送信すると、購読に成功すればサーバから以下のレスポンスが返されます。

{
  "success":true,
  "subscribe":"tradeBin1m",
  "request":{
    "op":"subscribe",
    "args":[
      "tradeBin5m"
    ]
  }
}

※実際の受信データでは改行は入っていません。読みやすさのために改行しています。

このレスポンスを受け取った際は、先ほど出てきたon_messageが呼び出されます。上記の応答内容は、on_messageの第二引数であるmessagejson形式の文字列として格納されています。

受信したメッセージを処理するon_messageの実装は、以下のようなイメージになるかと思います。

def on_message(self, ws: WebSocketApp, message: str):

    logger.debug('Received message -> ' + message)

    # JSON形式の文字列から辞書型に変換
    message = json.loads(message)  # type: dict

    # successのキーが存在する、かつsuccessのキーに紐づく値がTrueであれば、購読成功
    if 'success' in message and message['success'] and message['request']['op'] == 'subscribe':

        # トピックの購読に成功した時の処理
        ....

successのキーをinで確認しているのは、受信したメッセージが、クライアントからの要求に対する応答メッセージであることの確認のためです。

購読に成功したトピックのデータを受信する

購読に成功すると、それ以降は、該当のトピックに更新があれば、都度サーバからデータが送られてきます。 例えば、tradeBin5mのトピックを購読した場合、以下のようなデータが送られてきます。

{
  "table": "tradeBin5m",
  "action": "insert",
  "data": [
    {
      "timestamp": "2018-08-12T13:10:00.000Z",
      "symbol": "XBTUSD",
      "open": 6240,
      "high": 6250,
      "low": 6237,
      "close": 6245,
      "trades": 2134,
      "volume": 12536109,
      "vwap": 6242.5869,
      "lastSize": 17111,
      "turnover": 200821656480,
      "homeNotional": 2008.2165648000002,
      "foreignNotional": 12536109
    }
  ]
}

symbolに通貨の情報が入っています。上記の場合、XBTとUSDのペアとなります。open, high, low, close が、ローソク足四本値を表しています。

購読対象の通貨を限定する

tradeBin5mのようにトピック名だけを指定して、購読要求を送信すると、BitMEXが取り扱う全ての通貨ペアの情報が送られてきます。 情報を受け取る通貨を限定したい場合は、トピック名の後ろに:<シンボル名>の文字列を付与したうえで、購読要求を送信します。

例えば、XBTUSDのみの5分足データを受け取りたいのであれば、tradeBin5m:XBTUSDと指定します。通貨ペアの指定方法は、他のトピックの場合も同様です。

認証が必要なトピックを購読する

先に示したトピックの一覧は、全て公開トピックであり、認証無しで購読可能なトピックです。ユーザーに紐づくデータを受信するには、認証が必要です。認証が必要となるトピックは以下です。

"affiliate",   // アフィリエイトの状況 (合計紹介ユーザーや支払い率等)
"execution",   // 個別執行 (注文ごとに複数の可能性)
"order",       // リアルタイムでの注文の最新状況
"margin",      // 最新アカウント残高と証拠金必要額に関する最新情報
"position",    // ポジションに関する最新情報
"privateNotifications", // 個人的通知 - 現在未使用
"transact"     // 入金/出金に関する最新情報
"wallet"       // ビットコインアドレス残高データ (合計入金・出金額等)

認証から購読までの流れとしては、まず、サーバへの接続が完了後、クライアント側からサーバに対して、必要なデータを送りつけて認証要求を行います。

すぐにサーバから認証の結果が返って来るので、認証に成功すれば、購読したいトピックに対して、購読要求を行います。その後は、購読したトピックのデータを受信できるようになります。

認証を行う

認証を行うには、APIキーや署名など、認証に必要なデータをサーバへ送信する必要があります。 タイミングとしては、サーバへの接続完了後である必要があるので、コールバックであるon_openが実行された際に、認証要求を行えば良いでしょう。

コード例

import urllib
import hmac
import hashlib

def signature(self, api_secret: str, verb: str, url: str, nonce: int) -> str:
    data = ''
    parsed_url = urllib.parse.urlparse(url)
    path = parsed_url.path
    if parsed_url.query:
        path = path + '?' + parsed_url.query
    message = (verb + path + str(nonce) + data).encode('utf-8')
    sign = hmac.new(api_secret.encode('utf-8'), message, digestmod=hashlib.sha256).hexdigest()
    return sign

def on_open(ws):

    # セッションの有効期限を5秒に指定(例なので短くしている)
    expires = int(time.time()) + 5

    # 署名を作成
    signature = signature(api_secret=<APIシークレット>, verb='GET', url='/realtime', nonce=expires)

    # 認証用の送信データを作成
    auth = {
        'op': 'authKeyExpires',
         'args': [<APIキー>, expires, signature]
    }

    # サーバへ送信
    ws.send(json.dumps(auth))

関数signatureで署名を作成しています。その後、APIキー、セッションの有効期限、署名をauthに詰め込んで、サーバへ送信しています。

認証結果の受け取り

認証要求を送信した後は、on_messageで、以下のような認証結果を受信します。

{
  "success":true,
  "request":{
    "op":"authKeyExpires",
    "args":[
      "<APIキー>",
      "<セッション有効期限>",
      "<署名>"
    ]
  }
}

ポイントは、successの値がtrueであることです。trueであれば、認証に成功しています。argsの中身は、こちらが送信したデータの内容が詰まっているだけです。 認証に成功したら、認証が必要なトピックに対して、購読要求を送信すれば、サーバから購読成功のレスポンスと、トピックの更新情報が送られて来るようになります。

受信したデータのフォーマット

各トピックで更新が発生するたびに送られてくるメッセージには、いくつかのフォーマットがあります。

  1. 接続成功時のメッセージ
  2. クライアントからの処理要求に対する応答
  3. Pong
  4. トピックのデータ
1. 接続成功時のメッセージ

前述したサーバ接続直後に受信するウェルカムメッセージ的なものがこれにあたります。

{
  "info": "Welcome to the BitMEX Realtime API.",
  "version": "2018-08-17T17:20:19.000Z",
  ... 省略 ...
}

私が使っている限りでは、接続成功時以外で同様のフォーマットのメッセージを受信したことはまだありません。

2. クライアントからの処理要求に対する応答

クライアント側からトピックの購読や認証の要求を送信した際には、以下のフォーマットのレスポンスを受信します。

{
    "success": <処理結果>
    "request": {
        "op": "<要求した操作>"
        "args": [
            "引数1",
            "引数2",
            ...
        ]
    }
}

「要求した操作」の部分は、トピックの購読の場合であれば、subscribe、認証の場合であれば、authKeyExpiresといった具合です。

argsの配列ですが、クライアント側から送信したメッセージのパラメータが格納されています。

argsを見たいことはあまり無いような気がします。opでリクエストの種別を判定して、successで成功しているかどうかを見るというくらいでしょう。

3. pong

クライアントとサーバ間の接続が切断されていないことの確認のために、クライアント側からpingを投げる場合があります。それに対する応答メッセージです。

pong

上記のとおり、特に構造は持っておらず、pongという文字列のみが送られてきます。

4. トピックの購読データ

各トピックを購読することによって受信するデータです。

ローソク足や板の情報、保有ポジション、残高など、全てこれに該当します。

どのトピックのメッセージでも、必ずtableactiondataという3つのキーが含まれており、これらの内容を見て情報の種別と更新内容を把握します。


{
  "table": "execution",
  "action": "insert",
  "data": [
    {
      "execID": "xxxxxxxx",
      "orderID": "xxxxxxxx",
      ....
    }
  ]
}
table

tableは、トピック名が格納されています。orderのトピックのデータであれば、orderpositionのトピックのデータであれば、positionといった具合です。tableの値を見ることによって、受信したメッセージのトピックを判断します。

action

actionは、トピックで発生したイベントを表しています。actionの値には、以下の4種類があります。

  • partial・・・ある時点でのトピックのデータの塊がこのpartialというactionで送られてきます。例えば、最初のサーバへの接続時に有効な注文が複数存在する状態であれば、それらの注文に関する情報がまとめてこのactionで送られてきます。partialを受信した後は、差分のみが送られてきます。

    また、partialには、トピックのデータのキー情報の定義も格納されています。このキー情報は、データの更新時などに、対象を特定するために必要なものです。

    注文情報を例とすると、{"table": "order", "action": "partial", "keys": ["orderID"], "types": {...のように、keysフィールドに注文IDであるorderIDが指定されています。

    キー情報は一つとは限りません。複数のフィールドによって構成される複合キーの場合もあります。例えば、tablepositionのメッセージであれば、データのキー情報は"keys": ["account", "symbol", "currency"],となっています。

  • insert・・・トピックのデータに追加があったことを表しています。ローソク足の追加や、注文の追加などが該当します。

  • update ・・・トピックのデータに追加があったことを表しています。ポジションの価格の変更や、注文の約定などが該当します。

  • delete・・・トピックのデータに追加があったことを表しています。

data

データ部になります。追加、更新、削除された内容そのものや、削除された情報のIDなどが格納されています。

actionが、insertであれば、データの全てのフィールドが格納されていますが、updateの場合は、データを特定するためのキー情報(注文IDなど)と更新されたフィールドのみがdataに格納されています。

通信エラー発生時の再接続

websocketによるクライアントとサーバ間の接続が、何らかの通信トラブルによって、切断されてしまう場合があります。

いつのまにか通信が切断されてボットが停止したままとなっては、困ってしまいます。通信に問題が発生した場合は、それを検知して、再度サーバとの接続処理を行う必要があります。

サーバへの再接続

通信に問題が発生した場合はon_errorが呼び出されるので、この関数でエラー処理を行った後、再接続処理を行います。

厳密には、再接続というよりプログラム自体の再実行となります。

import os
import sys

os.execv(sys.executable, [sys.executable] + sys.argv)

osモジュールとsysモジュールを使用して、実行中のPythonプログラムに実行時と同等の引数を指定して、再度実行します。

こうすることによって、プログラムが最初から処理をやり直すため、再度サーバへの接続処理が行われます。

以上になります。また追記していこうと思います。

一人暮らしはWiMAXで十分!光からUQ WiMAXのWi-Fiに乗り換えた話

f:id:mitsu3204:20180815163638j:plain

自宅のインターネット環境を、光回線をやめてUQ WiMAXWi-fiに乗り換え(一本化)ました。

やってみた結論としては、一人暮らしであればWiMAXWi-Fiのみで十分です。

UQ WiMAXの移行について、なぜ移行したのか?移行した結果どうなったのか?などを記しておきます。同じ事を検討している人の参考になれば幸いです。

目次

  • WiMAX以前の環境
  • なぜWiMAXにしたのか
  • WiMAX移行後
  • 速度制限がかかるとどんな状態なのか?
  • WiMAXへ乗り換えの結論

WiMAX以前の環境

環境と利用状況はこんな感じです。

NTT東日本フレッツ光のマンションタイプを利用していました。

回線速度は速い時でも、数十Mbps程度で、遅い時は、数Mbpsやそれ以下になる時もありました。(住宅の位置や他のユーザーの利用状況により異なります) 常に速度が求められるような使い方をしているわけでもなかったので、概ね問題はなく、たまに遅すぎてストレスを感じることがあるくらいでした。

構成的には、Wi-fiを利用するために、NTTのルーターからAppleAirMac Extremeに繋いで、Wi-fiを利用していました。有線は一切使用していなかったです。

なぜWiMAXにしたのか?

どうしても変えなくてはいけないというほどの強い理由があったわけではありません。 ただ、WiMAX移行前の環境には、以下のようにいくつかの不満がありました。

引越した際に開通工事などの手続きが不要

光がとかそういうことではなく、引っ越す際に発生するNTTとのやりとりが、とにかく無駄に思えて嫌でした。NTTが嫌いとかそういうことではなく、わざわざNTTに連絡したり、人に来てもらって回線を開通させる工事を行うなどが、純粋に面倒臭かったのです。もっと身軽になりたかったのです。

水道、電気、ガスどれも面倒ですね(特にガス)。部屋に機械でも設置しておいて、IDやクレカ読み取らせて、手続き完了にならないですかね。。

ポータブルWi-fiの場合は、端末さえあればネットワークに接続できるので、開通工事も不要であり、楽で良いですね。

持ち運べる

今や、自宅の内外問わず、どこにいっても快適なネットワーク環境が求められる時代です。

スマートフォンの回線でも決して遅くないですし、スマートフォンしか使わない人だったら、あまり持ち歩く必要ないのかもしれませんし、スマートフォンテザリングでノートパソコンからWi-fi接続することもできますが、通信速度や利用可能な通信量はWiMAXのほうが圧倒的に優れています

自分の日常としては、ポータブルWi-fiの持ち歩きが必須だったわけではないのですが、必要があればそれが可能になるというのは、メリットだと考えていました。

月々の利用料金が安くなる

NTTの回線(ひかり電話含む)と、プロバイダで合わせて、月々約5,500円程度かかっていました。私の場合、ひかり電話なんて使わなかったですし、ネットつなぐだけで、月々5500円も払っているのも高いのかもなんて思い出してしまっていました。 WiMAXにすると、月に約3,600円となります(利用料金2,944円+端末安心サポートプラス594円)。大差では無いですが、契約もシンプルになって、便利になったうえに、料金まで安くなるというのは十分にメリットがあります。

契約の際は、契約事務手数料として3,240円が必要です。ただ、WEBからの申し込んだのですが、キャンペーンでAmazonのクーポン1万円分のキャッシュバックがありましたので、初期費用は実質かかっていません

物理的なモノが少なくなり部屋がすっきりする

WiMAXの端末は、光回線ルーターと比較すると非常に小さなものであることや、電源コード以外に物理的な線は存在しないため、部屋が少しスッキリします。 AppleAirMACもあり、インターネットを利用するために、機器が二つもあるのは邪魔だと感じていました。

WiMAX移行前に感じていた不安

基本的に自宅では、光を使ってWiMAXなんて外出時にのみ利用するものだろうというイメージを持っていたので、それ一本で全て乗り切れるのかという不安もありました。

具体的には、接続が不安定だったり、実は速度が遅かったりしないのか?というのが気になりどころでした。

それと、ネットワークの使用量が多いと、速度制限がかかる点も気になっていました。

この速度制限というのは、3日間のネットワーク使用量の合計が10GBを超過すると、その翌日の18時頃から翌2時頃までの間、通信速度が約1Mbps程度に抑えられるというものです。 (場所にもよりますが、通常時は数十Mbps〜数百Mbps程度の速度になると思われます)

www.uqwimax.jp

とはいえ、やってみない事には何も変わらない&何もわからないままですし、自宅のインターネット回線を完全に移行できるレベルのものでなかったとしても、持ち運び可能になる点で便利ではあるだろうということ、月3000円とちょっとなので、大したコストでも無いので契約してみました。

WiMAX移行後

結論からいうと、 UQ WiMAXに移行して大きな問題はありませんでした。UQ WiMAX契約後、1ヶ月程度経過後に不要となった光回線は解約しました。

購入した端末

これです。

f:id:mitsu3204:20180808235256p:plain

  • 製品名:Speed Wi-Fi NEXT W04
  • 重さ:140g
  • サイズ:約H130×W53×D14.2mm
  • Wifi規格:IEEE802.11ac/n/a(5GHz帯)、11n/g/b(2.4GHz帯)

利用方法

外にはあまり持ち出していなくて、ほとんど自宅用のWi-fiとして利用しています。WiMAXの端末のバッテリーは何日間も持つものではないので、自宅での利用時は常にコンセントに挿しっぱなしの充電状態で使っています。

速度

様々な時間帯に分けて定期観測したわけではないですが、自宅で使っていて数回測ってみた時は、ダウンロード(下り)で数十Mbps〜100Mbpsくらいです。

動画などの大きいデータのストリーミングも全く遅延しないし、ダウンロードもかなり速いです。

光回線の時は、結構遅い場合が頻繁にあったので、UQ WiMAXへ移行したことによって、速度に関するストレスはなくなりました。

たまに速度制限がかかる

前述したとおり、前日までの3日間の合計利用量が10GBを超えると、当日は1Mbps程度に速度が抑えられてしまいます。

私がWifiを利用している端末は、PC、スマホiPadKindle、Chromecast、スマート家電の5つです。PCとスマートフォンは、毎日利用しますし、常にネットワークに繋がった状態です。

ただ、ネットワーク利用量が多いであろうYouTubeNetFlixなどの動画アプリやサービスは、利用はするものの頻繁には使わない(毎日使うことはない)という状況です。よって、契約前から、制限については認識はしていましたが、自分のインターネットの利用状況的にそうそう制限に引っかかることはないだろうと考えていました。

ですが、実際には、月に数日程度は制限がかかっています。

「あれ、今日通信が遅いな」と気づく時があって、ネットワーク速度を測ってみると、約1Mbpsとなっており、WiMAXの制限かかっていることがわかります。

※2018/8/22追記 希望する人には、データ量制限が発生する際に当日のお昼ごろにメールで通知してくれるサービスをやっているそうです。

ちなみに、通信速度を測る際は、以下のサービスを利用しています。 NETFLIXが提供している回線速度測定ツールで、ブラウザでアクセスすると、すぐ計測が始まります。シンプルで便利です。

fast.com

どういう使い方をした際に、速度制限がかかっているかというと、大体は、NETFLIXで1時間や2時間などの動画を一本、二本程度閲覧した場合です。高画質設定で見ているので、動画一本で数GB使用しているのかもしれません。 ただし、動画だけが原因の大半とは限りません。私の日常的な使い方でそこそこ消費しているところに、動画でとどめを刺しているのかもしれません。また、NETFLIXで動画を見たからといって、常に速度制限を食らうわけではありません。速度制限が発動するのは、月の2、3回といったところです。

NETFLIXは画質を設定できるので、画質を下げれば、ネットワークの通信量も少なくなり、速度制限にかかることも減るでしょう。私は、たまにしか制限はかからないため、気にせず高画質設定のまま利用しています。

速度制限がかかるとどんな状態なのか?

速度制限がかかる条件を満たしたとしても、制限がかけられるのは、18時頃から翌2時頃までの間のみとなります。制限がかかった状態では、以下のようになります。

ウェブの閲覧は待ち時間が多少長くなる

リンクなどをクリックして、ページが表示されるまでの待ち時間は、はっきりと体感できるレベルで長くなります。 とはいえ、1Mbpsというのは、決して使えないほどのレベルではないです。画像がたくさん表示されるウェブサイトなどは、ストレスを感じるかもしれません。

動画は画質が悪くなる

1Mbpsに速度制限がかかった状態でNETFLIXの動画を見ようとすると、見れなくはないですが、画質が悪い状態となります。 綺麗な画質で見たい動画の場合は、速度制限中を避けたほうがよいですが、画質を気にしないのであれば、気になるのは最初のほうだけで、途中で目が慣れてしまうと思います。

アプリや電子書籍などのダウンロードは相当遅く感じる

アプリや電子書籍などのように、一つのコンテンツが数十メガバイトもあるようなものは、ダウンロードにかなりの時間がかかってしまうようになり、ストレスを感じます。どの程度時間がかかるかは、コンテンツにもよりますが、私は、ダウンロードを諦める時もありました。

WiMAXへ乗り換えの結論

環境によっては、私と同じ様にUQ WiMAXのほうが速いということもあるかと思います。

速度制限の懸念があるため、毎日動画を長時間見る人はUQ WiMAX一本化というのはさすがにやめたほうが良いと思われます。

また、私は一人暮らしなので自分の利用量が全てですが、家族数人で暮らしている場合は、動画の利用有無に関わらず、UQ WiMAXへの一本化は通信量的に現実的ではないでしょう。(家族で使っていたら、持ち出しもできないでしょうし・・・)

一人暮らしで、WEBは頻繁に使うけれども、動画はたまに見る程度の人であれば、月々の料金も安くなるし、UQ WiMAXへの乗り換えも考えても良いかと思います。