Home > コンピュータ | > Java

Java Archive

自動トレーディングへの道(6)

自動トレーディングへの道がGoogleに

つい最近までGoogleで「自動トレーディング」で検索すると最上位にヒットしていました。継続は力なり。でもまた下に下がっていきました。湯は熱し続けなければやがて水に戻る。

Context側を作る

public interface IContext
{
 public abstract void login( String ID, String Password );
 public abstract void logout();
}

とりあえずログインとログアウトのみ。続いてこれを実装する側

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import org.apache.oro.text.perl.Perl5Util;

public class Monex extends JFrame implements IContext, ActionListener {
	// 初期状態はログインされていない
	private IState state = MonexLoggedOutState.getInstance();

	private static final long serialVersionUID = 1L;

	private TextField textClock = new TextField(60);
	private TextArea textScreen = new TextArea(10, 60);
	private Button buttonLogin = new Button("ログイン");
	private Button buttonLogout = new Button("ログアウト");
	private Button buttonQuote = new Button("株価取得");
	private Button buttonExit = new Button("終了");

	private String hash = "";
	private String sid = "";
	private String id = "";

	public Monex(String title) {
		super(title);
		setBackground(Color.lightGray);
		setLayout(new BorderLayout());

		// textClock配置
		add(textClock, BorderLayout.NORTH);
		textClock.setEditable(false);

		// textScreen配置
		add(textScreen, BorderLayout.CENTER);
		textClock.setEditable(false);

		// ボタンを配置
		Panel panel = new Panel();
		panel.add(buttonLogin);
		panel.add(buttonLogout);
		panel.add(buttonQuote);
		panel.add(buttonExit);

		add(panel, BorderLayout.SOUTH);

		pack();
		setVisible(true);
		setBounds( 10, 10, 400, 200 );
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		buttonLogin.addActionListener(this);
		buttonLogout.addActionListener(this);
		buttonQuote.addActionListener(this);
		buttonExit.addActionListener(this);

		textClock.setText(state.toString());
	}

	// ボタンが押されたとき
	public void actionPerformed(ActionEvent e) {
		System.out.println(e.toString());

		if (e.getSource() == buttonLogin) {
			state.login(this, "aa", "aaa");
			return;
		}

		if (e.getSource() == buttonLogout) {
			state.logout(this);
			return;
		}

		if (e.getSource() == buttonQuote) {
			state.quote(this);
			return;
		}

		if (e.getSource() == buttonExit) {
			System.exit(0);
			return;
		}

		System.out.println("?");
	}

	// FSMを更新する
	public void update() {
		String msg = state.toString();

		System.out.println(msg);
		textClock.setText(msg);
	}

	public void changeState(IState nextState) {
		System.out.println(this.state + "から" + nextState + "へ状態が変化しました\n");
		this.state = nextState;
		textClock.setText(state.toString());
	}

	public void getStockInformation(int stockCode) {
		// TODO 自動生成されたメソッド・スタブ

	}

	public void login(String ID, String Password) {
		state.login(this, ID, Password);
	}

	public void logout() {
		state.logout(this);
	}

	public void setSessionID(String responseBody) {
		Perl5Util pu = new Perl5Util();
		String pattern = "/<A href=\"(https://ot2.qhit.net.*?)\".*?ACCESSKEY=\"1\"/";
		if (pu.match(pattern, responseBody)) {
			String result = pu.group(1);
			pattern = "/ot2\\.qhit\\.net/(.*?)/.*?sid=(.*?)&.*?&id=(.*?)$/";

			if (pu.match(pattern, result)) {
				hash = pu.group(1);
				sid = pu.group(2);
				id = pu.group(3);
			}
		}
	}

	public String makeSeaechStockCodeURL(int code) {
		String url = "https://ot2.qhit.net/";
		url += hash;
		url += "/mb/quote.cgi?F=Idetail0&QCODE=";
		url += code;
		url += "&MKTN=T&rf=r&SID=&ID=";
		url += id;
		url += "&DM=www&mode=";

		return url;
	}
}

実行結果

ログインしていない状態

まず起動するとログインしていない状態になる。この状態を管理しているのはMonexLoggedOutStateで、このlogout()の実装は

public void logout(Monex context) {}

になっているため、ここで「ログアウト」をクリックしてもなにも起きない。代わりに「ログイン」を押すとこちらは実装されているので処理を行ってログイン状態に遷移する。ログイン時にはセッションIDかな、何か謎の文字列を記録する。この辺は「自動トレーディングへの道(5)」にある。ログインに成功すると「ログインしていませんからログインしていますへ状態が変化しました」とコンソールに出力される。

ログインしている状態

この状態で「ログイン」を押しても、やはり空っぽの実装なので何もしない。株価取得は動く。

java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=株価取得,when=0,modifiers=] on button1
(9433) 東証一部
KDDI
02/29 15:00
現在値:640000 +13000(+2.07%)
売り気配:641000(20)
買い気配:640000(855)
始値:626000
高値:644000
安値:625000
出来高:40107

(9202) 東証一部
ANA
02/29 15:00
現在値:435 -1(-0.22%)
売り気配:436(77000)
買い気配:435(120000)
始値:433
高値:437
安値:431
出来高:3237000

(3436) 東証一部
SUMCO
02/29 15:00
現在値:2375 -195(-7.58%)
売り気配:2385(10300)
買い気配:2375(11100)
始値:2490
高値:2505
安値:2370
出来高:2716300

(5411) 東証一部
JFEHD
02/29 15:00
現在値:4740 +10(+0.21%)
売り気配:4750(6700)
買い気配:4730(38100)
始値:4810
高値:4820
安値:4570
出来高:5786000

(8058) 東証一部
三菱商
02/29 15:00
現在値:3270 -70(-2.09%)
売り気配:3270(114100)
買い気配:3260(99400)
始値:3240
高値:3280
安値:3200
出来高:10629100

(8316) 東証一部
三井住友
02/29 15:00
現在値:772000 -33000(-4.09%)
売り気配:772000(121)
買い気配:771000(38)
始値:785000
高値:789000
安値:762000
出来高:46122

(7974) 東証一部
任天堂
02/29 15:00
現在値:53300 -3200(-5.66%)
売り気配:53400(100)
買い気配:53300(2700)
始値:54200
高値:54400
安値:53100
出来高:46300

NetBeans 6.0

これはいいかも知れない。以前、ちょっとだけ使ってEclipseと比較してこれはダメだなと安易に決めてしまったのだけど、GUIビルダだけでも使おうとNetBeans 6.0.1をインストールしてみた。

マイコミジャーナルに「ついに登場! NetBeans 6.0 – その新機能を徹底解剖する」という記事がある。大ざっぱなことはこれでわかる。

さっきインストールしたばかりだけど、少し使ってみようと思う。

自動トレーディングへの道(5)

再びMonexLoggedOutState

MonexLoggedOutStateのlogin()で

context.setSessionID(HTTP.getInstance().getResult());

とした。これはIContext側のクラスに(例によって<を全角にしています)

public void setSessionID(String responseBody) {
	Perl5Util pu = new Perl5Util();
	String pattern = "/<A href=\"(https://ot2.qhit.net.*?)\".*?ACCESSKEY=\"1\"/";
	if (pu.match(pattern, responseBody)) {
		String result = pu.group(1);
		pattern = "/ot2\\.qhit\\.net/(.*?)/.*?sid=(.*?)&.*?&id=(.*?)$/";

		if (pu.match(pattern, result)) {
			hash = pu.group(1);
			sid = pu.group(2);
			id = pu.group(3);
		}
	}
}

というものがある。ログイン後はURLに謎の文字列を含むものが出てくる。これを間違えると処理されず、おそらくセッションIDではないかと思う。URLもmonexではなくot2.qhit.netになる。

というわけで”https://ot2.qhit.net”を含むURLでACCESSKEYが1であるものを正規表現で抜き出して、さらにその中から意味不明の文字列を3つ取り出す。意味がわからないので、とりあえずhash, sid, idとしておく。これはログイン後の状態からmakeSeaechStockCodeURL()で利用する。

public String makeSeaechStockCodeURL(int code) {
	String url = "https://ot2.qhit.net/";
	url += hash;
	url += "/mb/quote.cgi?F=Idetail0&QCODE=";
	url += code;
	url += "&MKTN=T&rf=r&SID=&ID=";
	url += id;
	url += "&DM=www&mode=";

	return url;
}

ログイン後の状態

getPrice()では

// 株価ページを取得
String url = context.makeSeaechStockCodeURL(code);
GetMethod get = new GetMethod(url);
HTTP.getInstance().executeMethod(get);
get.releaseConnection();

でHTMLを取得し、これを解析していく。GETするURLはmakeSeaechStockCodeURL()で作る。多くの情報をバラバラに管理するのは面倒くさいのでStockInformationクラスを作成する。

package utilities;

public class StockInformation
{
	private Exchange exchange;	// 取引所
	private String name;			// 銘柄
	private String date;			// 日付
	private String time;			// 時刻

	private int code;			// コード(4桁)
	private int price;			// 株価
	private int id;				// 前日比 increase-decrease;
	private double idr;			// 前日比(率)increase-decrease rate
	private int sell;			// 売り気配;
	private int sellAmount;		// 売り枚数
	private int buy;				// 買い気配;
	private int buyAmount;		// 買い枚数
	private int opening;			// 始値
	private int high;			// 高値
	private int low;				// 安値
	private int volume;			// 出来高
	private int closing;			// 終値(決まるまでは0にしておく)

	public StockInformation()
	{

	}

	// getter, setterをたくさん作る	

	public String toString()
	{
		String result = "(" + code + ") " + Exchange.name(exchange) + "\n";
		result += name + "\n";
		result += date + " " + time + "\n";

		result += "現在値:" + price;
		if( id > 0 )
			result += " +" + id + "(+" + idr + "%)\n";
		else
			result += " " + id + "(" + idr + "%)\n";

		result += "売り気配:" + sell + "(" + sellAmount + ")\n";
		result += "買い気配:" + buy + "(" + buyAmount + ")\n";
		result += "始値:" + opening + "\n";
		result += "高値:" + high + "\n";
		result += "安値:" + low + "\n";
		result += "出来高:" + volume + "\n";

		return result;
	}
}

あとはゴリゴリ正規表現を駆使してStockInformationに値を入れる。この際、面倒くさいのは”654,000″のような文字列は普通にInteger.parseInt()すると例外を投げてしまうので下処理がいる。そのためのユーティリティクラスを作る。

public class NumUtilities
{
	public static int toInt(String str)
	{
		return Integer.parseInt(trim(str));
	}

	public static double toDouble(String str)
	{
		return Double.parseDouble(trim(str));
	}

	public static String trim(String in)
	{
		char [] c = in.toCharArray();
		StringBuffer result = new StringBuffer();

		for( int i = 0; i < c.length; i++ )
			if(('0' <= c[i] && c[i] <= '9') || c[i] == '-' || c[i] == '.' )
				result.append(c[i]);

		return result.toString();
	}
}

Home > Java

Search
Feeds
Meta

Page Top