LoginSignup
0

posted at

updated at

【v38】マージツールとテクニカル指標計算ツールを統合する

はじめに

過去記事は「auカブコム証券のkabuステーションREST APIに関する記事一覧」。

マージツール(バッチ用とリアルタイム監視)とテクニカル指標計算ツールを別々にリビジョンを振って管理していたが、2つのツールを統合する。

ベースとなるソース

v36._r5とv37._r3とv37._9のソースをベースに、v38._r10を作成する。

ゴール

クラスをr10に統一する。
入出力ファイルはr5のまま変更しない。
main()は当面はこのまま変更しないで使ってみて、統合するか決める。
クラス名の一覧を文字列定数でソース内に埋め込んでいるので、propertiesファイルに設定する。

パッケージ構成

いままでフラットにv36.*にクラスを並べていたが、プラグインなどをサブパッケージに分ける。

  • bean: エンティティ
  • c: テクニカル指標計算プラグイン
  • factory: プラグインインスタンス生成の管理
  • i: インターフェイス
  • m: マージプラグイン

実装

propertiesファイル

Propertiesクラスにロードすると、Hashtableで管理されているため、キー順がバラバラになってしまう。
そこで、キーに整数(ソートキーのため、連続してなくてもよい)を振り、読み込んだ後に、TreeMapでキーを整数順に並べて、配列に代入する。

1=1m
2=3m
3=5m
4=10m
5=15m
6=20m
7=30m
8=60m
	private static String[] barNames;

	static {
		try {
			InputStream is = BarNameFactory_r10.class.getResourceAsStream("BarNameFactory_r10.properties");
			Properties prop = new Properties();
			prop.load(is);
			Map<Integer, String> map = new TreeMap<>();
			for (String key : prop.stringPropertyNames()) {
				String val = prop.getProperty(key);
				map.put(StringUtil.parseInt(key), val);
			}
			barNames = new String[map.size()];
			int i = 0;
			for (Integer key : map.keySet()) {
				String val = map.get(key);
				barNames[i++] = val;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

マージクラスは2つの配列の順番を揃えてソースに記述していたが、key=valueで記述する。
これもPropertiesにロードすると、キー順はバラバラになるので、上記のbarNamesを使う。
classNameMapに代入しなくてもgetProperty()を直接呼び出してもよかったが、propertiesファイルの記述方法を変更したときのために、解析部分と利用部分を分けておく。

1m=v38.m.MergeChartData1m_r10
3m=v38.m.MergeChartData3m_r10
5m=v38.m.MergeChartData5m_r10
10m=v38.m.MergeChartData10m_r10
15m=v38.m.MergeChartData15m_r10
20m=v38.m.MergeChartData20m_r10
30m=v38.m.MergeChartData30m_r10
60m=v38.m.MergeChartData60m_r10
	private static Map<String, String> classNameMap = new TreeMap<>();

	static {
		try {
			InputStream is = BarNameFactory_r10.class.getResourceAsStream("MergeChartDataFactory_r10.properties");
			Properties prop = new Properties();
			prop.load(is);
			for (String key : prop.stringPropertyNames()) {
				String val = prop.getProperty(key);
				classNameMap.put(key, val);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static Map<String, Constructor<MergeChartData_r10>> consMap = new TreeMap<>();

	static {
		String[] barNames = BarNameFactory_r10.getBarNames();
		for (int i = 0; i < barNames.length; i++) {
			String key = barNames[i];
			String name = classNameMap.get(key);
			try {
				@SuppressWarnings("unchecked")
				Constructor<MergeChartData_r10> cons = (Constructor<MergeChartData_r10>) Class.forName(name).getDeclaredConstructor(String.class);
				consMap.put(key, cons);
			} catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

テスト

eclipseから実行すると、問題なくpropertiesファイルを読んだが、バッチから起動するとInputStream isがnullでNPEが発生する。
getResourceAsStream()はクラスパスから、パッケージパス(v38.factory)の階層を探すので、クラスパスがclassesならば、classes\v38\factoryに3つのpropertiesファイルを配置する。

追記:DB_FILENAME,CHART_TXT_FILENAMEをMergeChartDataCommon_r10で扱う

既にCalcCoordinator_r10はCHART_TXT_FILENAME = "ChartData%s_r5.txt"のように動的にファイルを生成しているので、マージツール側もMergeChartDataCommon_r10側でDB_FILENAME = "ChartData%s.db"やCHART_TXT_FILENAME = "ChartData%s_r5.txt"のように動的にファイル名を生成する。

コンストラクタMergeChartDataCommon_r10(String name, String bar)に変更する。
これにより、各具象クラスのファイル名の文字列定数を削除する。

public class MergeChartData1m_r10 extends MergeChartDataCommon_r10 implements MergeChartData_r10 {
	public MergeChartData1m_r10(String name, String bar) {
		super(name, bar);
	}

	protected String search(String time) {
		String startTime = ChartTime1mLogic.search(time);
		return startTime;
	}
}

追記:現物のデータに対応

先物OPしか対象としていなかったので、あまり現物のデータの中身を確認していなかったが、
先物は8:45-15:15に対して、現物や指数は9:00-15:00のため、分足の種類によって、誤ったマージデータとなる。

  • 10分足:8:45,8:55,9:05のため、9:00は8:55となる
  • 20分足:8:45,9:05のため、9:00は8:45となる
  • 30分足:8:45,9:15のため、9:00は8:45となる
  • 60分足:8:45,9:45のため、9:00は8:45となる

現物用に別途テーブルを用意し、search()にsuper.isFuture()を渡して区別する。

追記:CalcIndicatorCommon_r10に共通化

マージツールのMergeChartDataCommon_r10クラスと同様に、共通メソッドをCalcIndicatorCommon_r10クラスにまとめる。

追記:MainChartCalendar_r10に統一

v27.MainChartCalendar_r3をベースに、v38.MainChartCalendar_r10を作成する。
MainChartDataDaily_r3、MainChartDataHourly_r3も同様にMainChartDataDaily_r10、MainChartDataHourly_r10を作成する。

追記:MainChartDataMigrate_r10

短いx分足のDBデータをもとに、整数倍のy分足のDBデータを生成する。
xとyの組み合わせ(x -> y)は以下のとおり。
30m -> 60mのファイル名を、ChartData60m_30m.dbのように命名する。

  • 1m -> 3m,5m,10m,15m,20m,30m,60m
  • 3m -> 15m,30m,60m
  • 5m -> 10m,15m,20m,30m,60m
  • 10m -> 20m,30m,60m
  • 15m -> 30m,60m
  • 30m -> 60m

追記:MainChartDataIntegrate_r10

MainChartDataMigrate_r10で生成された複数のDBデータをマージして、ChartData60m_r10.dbファイルを作成する。
ChartData60m.dbを優先し、存在しない日時のデータをChartData60m_1m.dbなどからコピーする。

これを読み込むマージツール側がChartData60m.dbの代わりにChartData60m_r10.dbに変更する。
マージ後ファイル名もr5からr10に変更する。

追記:古いデータを削除

マージツールは24*60件、テクニカル指標は60件の最新データのみをファイル保存する。

追記:CalcIndicatorCommon_r10のcalcIndicator(),printIndicator()に統一

7種類のテクニカル指標を作成して、execute()が同じ形のため、CalcIndicatorCommon_r10クラスへ移動し、フレームワーク化する。
個別のcalcSma()やprintSma()をcalcIndicator()とprintIndicator()に統一する。

追記:IndicatorCodeによる紐づけ

この後、イベントトリガーを各テクニカル指標ごとに用意しようとすると、それぞれにIDがないと紐づけができない。
マージクラスは、MergeChartDataFactory_r10.propertiesのキーの値を、BarNameFactory_r10.propertiesに定義していたが、同様にpropertiesファイルを追加するのは面倒なので、列挙体を定義し、具象クラス内に埋め込むことにする。

public enum IndicatorCode {
	SMA(1), BollingerBands(2), MACD(3), HV(4), Pivot(5), DMI(6), Parabolic(7);
public abstract class CalcIndicatorCommon_r10 implements CalcIndicator_r10 {
	abstract public IndicatorCode getCode();
public class CalcIndicator1_r10 extends CalcIndicatorCommon_r10 implements CalcIndicator_r10 {
	public IndicatorCode getCode() {
		return IndicatorCode.SMA;
	}

追記:BarCode列挙体をマージプラグインに埋め込む

IndicatorCode列挙体と同様にマージプラグインにもBarCode列挙体を埋め込む。
v40以降でイベントトリガーのメタ情報にマージプラグイン(足名)を追加する際に使う。

すでに定義されているMergeChartDataFactory_r10.propertiesのキー(=String bar)がBarCode.codeと同じ文字列となっているので、矛盾がないことをファクトリーでチェックする。将来、MergeChartDataFactory_r10.propertiesのキーを連番(1, 2, 3,...)に変更してもよい。

追記:MainChartDataMigrate_r10が出力するファイルの拡張子を変更

MainChartDataMigrate_r10がChartData10m_1m.dbのようなファイル名で出力していたが、中間ファイルなので、拡張子をtmpに変更する。
MainChartDataIntegrate_r10は*.tmpを処理対象とする。

(なお修正前の、拡張子がdbだった場合、ChartData10m.dbを最初に読み、ChartData10m_1m.dbなどを読みマージするが、出力ファイルChartData10m_r10.dbも読んでマージしてしまっていた。拡張子tmpに限定することで、出力ファイルは読まなくなる)

追記:テクニカル指標計算クラスにファイル出力でなくレコード文字列の変換を任せる

CalcIndicatorCommon_r10.printIndicator(PrintWriter)をabstractにして、各具象クラスにファイル出力をさせていたが、各レコードが独立しているため、1レコードづつCalcIndicatorInfo_r10から文字列を変換させ、CalcIndicatorCommon_r10側がファイルに保存する。

この修正をrestserverに持っていき、ファイル保存をRepositoryクラスに変えられるようにする。

追記:同じChartData.csvの読み込みを1回にする

マージツールは、4本値データとTickデータを読んで処理するが、Tickデータは同じ銘柄(ディレクトリ)内のChartData.csvを、各x分足を処理するごとに毎回読んでいたが、同じ内容なので最初の1回だけにする。

		List<String> lines = null;
		for (String key : BarNameFactory_r10.getBarNames()) {
			MergeChartData_r10 merge = mergeMap.get(key);
			lines = merge.execute(lines);
		}

日付誤りのstdout logを置換前と置換後の2行にする。prevは前のレコードの日付であり、置換前の日付ではない。
修正前では、このログがx分足の数だけ複数回出力されていた。

Warning: REPLACE line=2022-08-09 00:00:00,27855.0,181357.0, new=2022-08-10 00:00:00, prev=2022-08-09 23:59:58
Warning:     NEW line=2022-08-10 00:00:00,27855.0,181357.0

追記:MergeChartInfo_r10をv38.beanからbeanに移動

今後、おそらく変わらないので、v38からルート直下のbeanにクラスを移動する。
v45.bean.MergeChartInfo_r17は変更ないため、v45もMergeChartInfo_r10を参照する。

追記:CalcIndicatorInfo_r10をv38.beanからbeanに移動

MergeChartInfo_r10と同様にCalcIndicatorInfo_r10もbeanへ移動する。
v38.beanパッケージ内のクラスは無くなる。

追記:CalcCoordinator_r10のマージデータ受け入れをMergeChartData_r10からMapに変更

execute(MergeChartData_r10)を後から追加して、MainWatchChartDataでマージ後のデータを受け渡していたが、インターフェイスを渡すと、revが上がっていくと、どちらも上げていく必要がある。最終的にreadChartData()の中で、getChartMap()で全データを取得するので、MainWatchChartDataがgetChartMap()を呼んで、Mapを渡す。

本来ならば、MergeChartInfo_r10をMap, Listなどで管理するクラスがあった方がよい。

追記:ソースをarchiveブランチへ移動

最新版に移行し、もう使われることはないので、アーカイブする。

githubソース

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0