auカブコム証券のkabuステーションREST APIをjava(generated by the swagger code generator)で叩く


はじめに

前記事
auカブコム証券のkabuステーションREST APIをcurlで叩く

curlで叩くことが出来たら、やはりちゃんとしたエラー処理やループ処理も制御したいものです。
Excel, JavaScript, Python, C#しかサンプルがないので、javaで作ってみる。

事前準備

curlで叩いて、TOKENが取得できる状態にしてください。

ツール

  • java 8 (java 11はダメぽいよ)
  • eclipse (テキストエディタでもなんでも可)
  • maven 3 (gradleもあるけど、maven派なのだ)
  • swagger editor (オンラインでもdockerでも)

swagger editor

swagger editor

むかしはオンラインで使っていたが、dockerでlocalhostに起動した。

docker pull swaggerapi/swagger-editor
docker run -d -p 80:8080 swaggerapi/swagger-editor

Webブラウザで http://localhost/ にアクセスする。
左側のyamlエディタに以下のURLのデータを貼り付ける。
kabuステーションAPI

なんかエラー出てる。一部使えないかも(未確認)。

Errors
 
Semantic error at paths./margin/marginpremium/{symbol}
Parameter names are case-sensitive. The parameter named "Symbol" does not match the case used in the path "/margin/marginpremium/{symbol}".
Jump to line 3089

[Generate Client]メニューから[java]を選ぶ。
ダウンロードしたjava-client-generated.zipを適当に解凍する。

eclipse

java-client-generatedを解凍したディレクトリから、mavenを実行してみる。

mvn clean install

ビルドできるようなら、eclipseプロジェクトを生成する。

mvn eclipse:eclipse

.projectが出来るはずなので、eclipseからImport (General/Existing Projects into Workspace)する。
(あれ、最初からmavenプロジェクトをインポートすればいいのか・・・)。

eclipseがJava 11になっていて、エラーとなるので、Java 8に落とした。

認証API

JUnitテストスタブソースにio.swagger.client.api.AuthApiTestクラスがあるので、ちょっと修正して、認証APIを叩いてみる。

@Ignoreをコメントアウト

@Ignoreなんて初めて見た。これがあると、テスト実行されない。。。一番悩んだ。

RequestTokenにパスワードを設定

追加した行を分かりやすくしています。1行目と2行目は普通は1行にします。
パスワードを渡すコンストラクタは用意されていませんでした。

RequestToken body = null;
body = new RequestToken();           // add
body.setApIPassword("YourPassword"); // add
TokenSuccess response = api.tokenPost(body);

レスポンスの確認

TokenSuccess#toString()が実装されているので、stdoutに出力してみる。
getResultCode()がInteger型を返すので、assertEquals(Object, Object)のためint型に変えてます。
assertEqualsとassertNotNullを、static importしてください。

// TODO: test validations
System.out.println(response);                     // add
assertEquals(0, (int) response.getResultCode());  // add
assertNotNull(response.getToken());               // add

JUnit実行

AuthApiTestクラス単体、もしくは、他も@Ignoreが付いているので、src/test/java全体をJUnit実行する。

class TokenSuccess {
    resultCode: 0
    token: 0544c15ecf4e49f6ac0280d959510b52
}

時価情報・板情報API

TOKENが取れたところで、KDDI株式会社の情報を取得してみようか。
InfoApiTestクラスにboardGetTest()メソッドがあるけど、他にいろいろあって、テストPGMを作りたいわけではないので、Mainクラスを用意します。

public class Main {
	private static AuthApi authApi = new AuthApi();
	private static InfoApi infoApi = new InfoApi();
	
	public static void main(String[] args) throws ApiException {
		String token = null;
		{
			RequestToken body = new RequestToken();
	        body.setApIPassword("YourPassword");
	        TokenSuccess response = authApi.tokenPost(body);
	        System.out.println(response);
	        token = response.getToken();
		}
		{
	        String X_API_KEY = token;
	        String symbol = "9433@1";
	        BoardSuccess response = infoApi.boardGet(X_API_KEY, symbol);
	        System.out.println(response);
	    }
	}

}

どちらもapi変数だったので、区別するため、authApiとinfoApiに変更した。
TokenSuccessとBoardSuccessのresponse変数名を変えてもいいけど、だんだん面倒になるので、ブロックでスコープを限定した。
きっと、認証済TOKENをシングルトンに保存してどこからでも参照できるようにするとかするだろうけど、細かいことを好きに修正してくだされ。

実行すると、

class TokenSuccess {
    resultCode: 0
    token: c8b405f2ec584b12aca095ff06dd2c1d
}
class BoardSuccess {
    symbol: 9433
    symbolName: KDDI
    exchange: 1
    exchangeName: 東証1部
    currentPrice: 3972.0
:
}

おまけ(未確認)

kabuステーションを再起動しない限り(毎朝、強制終了されるらしい)、認証TOKENは使いまわせるらしいですが、逆に毎回認証APIを呼んで、問題があるのかは知りません。
ただ、認証APIを呼んで取得したTOKEN1より前に取得したTOKEN0では、

{"Code":4001009,"Message":"APIキー不一致"}

が返るので、解放されている気がします。

javaプロセスを常駐させてしまえば、TOKENをメモリに入れて使いまわすことは比較的簡単ですが、夜間に不意に落ちたときの復旧のことを考えると、タスクスケジューラで定期的に実行する方法もメリットがあります。毎回TOKEN発行を避けるのなら、ファイル等に保存することになりますが、「APIキー不一致」となる可能性もあり、swaggerの自動生成したAPIをラップした気の利いたAPIを用意するとよいかも。

追記:検証用APIに切り替え方法

そーいえば、本番用ポート18080と、検証用ポート18081と2つ開いているけど、どこにも指定していないな・・・。
ということで探したら、
io.swagger.client.ApiClientクラスに本番用をベタ書き。

private String basePath = "http://localhost:18080/kabusapi";

検証用にしたい場合は、setBasePath()に "http://localhost:18081/kabusapi" を渡せばいいようです。
クセがあり、例えばAuthApiでは、Configurationからデフォルトクライアントを取得しているので、インスタンス生成前にこのデフォルトを変更します。

public AuthApi() {
    this(Configuration.getDefaultApiClient());
}

static変数の初期化の前に実行したいので、staticイニシャライザを使います。

static {
	ApiClient client = new ApiClient();
	client.setBasePath("http://localhost:18081/kabusapi");
	Configuration.setDefaultApiClient(client);
}

private static AuthApi authApi = new AuthApi();
private static InfoApi infoApi = new InfoApi();

本番用パスワードと検証用パスワードを区別していたので、本番用ではエラーとなり、検証用で認証が成功しました。