レベル: 中級 木村 桂 (kimuc@jp.ibm.com), ソフトウェア事業, IBM
2007年 4月 06日 前回の紹介記事では、ゴールデンクロスによって購入する銘柄の選別を行う例を紹介しました。ただ、せっかく上昇が見込める株を選別して買ったとしても、ただやみくもに決済しては選別した意味も半減してしまいます。
今回は前回の続きとして、決済するタイミングを調整する例を紹介します。具体的には「ゴールデンクロスのタイミングで購入した銘柄を、デッドクロスのタイミングで決済する」というロボットを作る例を紹介します。また今回からはスクリーニング処理を実装して、カブロボには実行タイミングによって以下のような動作をさせるよう変更します:
- 市場が閉まった営業日の夜にスクリーニング処理を行い、各銘柄ごとに日足の13日および34日の移動平均線によるゴールデンクロス・デッドクロスが発生しているかどうかを調べる。
- 翌営業日の午前場にポートフォリオ内の各銘柄毎に、前営業日夜にデッドクロスが発生していたかどうか調べる。所有しているポートフォリオ銘柄でデッドクロスが発生していたら決済する。
- また同じタイミング(翌営業日の午前場)に、前営業日夜にゴールデンクロスが発生していた銘柄を購入する。
平たく言えば「ゴールデンクロス買い、デッドクロス決済」を行うロボットになります。またここでのゴールデンクロス/デッドクロスの判断には13日と34日の日足移動平均を用いることになります。
もう一度準備作業
では早速新しいロボットを作って・・・と行きたいところですが、その前に1つ問題があります。カブロボの標準機能だけでは営業日や午前場・午後場をまたいでデータを記憶することができないのです。上記の例でいえば(1)で調べた結果を、翌営業日の(2)や(3)で使う時まで記憶させておくことができないのです。これが問題になります。
といっても全く解決方法がないわけではありません。カブロボ SDK の標準機能で用意されていないだけであって、足りないものは自分で用意すればよい、とも言えます。そこでまずはカブロボに日をまたいでデータを記憶させるための追加機能を用意することにします。
Eclipse を起動し、パッケージエクスプローラからプロジェクト名(今回の例では "Kaburobo")部分を右クリックし、 New → Class を選択します。
また、New Java Class のダイアログでは以下の様に指定して、Name 欄に "DwMemo"(お好きな名前で構いませんが、以下はこれと同じ指定をしている前提で紹介します) を指定します。また Superclass 欄は特に変更せずに Finish ボタンをクリックします。
パッケージエクスプローラ内に今作成したクラス: DwMemo.java が新たに追加されます。ダブルクリックしてこのファイルを開き、以下のように内容を記述して保存します(追加部分を赤字で示しました):
DwMemo.java の内容
import java.io.Serializable;
import java.util.ArrayList;
import jp.tradesc.superkaburobo.sdk.trade.data.Stock;
public class DwMemo implements Serializable{
private static final long serialVersionUID = 1L;
private ArrayList<Stock> buys = new ArrayList<Stock>();
private ArrayList<Stock> sells = new ArrayList<Stock>();
public ArrayList<Stock> getBuyList(){
return buys;
}
public ArrayList<Stock> getSellList(){
return sells;
}
public void setBuyList( ArrayList<Stock> buys ){
this.buys = buys;
}
public void setSellList( ArrayList<Stock> sells ){
this.sells = sells;
}
}
|
この DwMemo.java の詳しい内容は(カブロボの本質的な部分とは離れるので)紹介しませんが、簡単に言えばシリアライズという Java 言語の機能を利用して、上昇候補の銘柄と下落候補の銘柄それぞれの設定、および取り出しが、日をまたいでも行えるようにしたものです。この後に紹介していくカブロボではこの DwMemo を利用して開発します。
ここまでの一連の作業が完了すると、Eclipse のパッケージエクスプローラは以下のようになっています。今までに作成した DwRobot01.java, DwRobot02.java という2体のカブロボに加えて、上記で作成した DwMemo.java というファイルが加わっているはずです。
カブロボ製造
では、この DwMemo の使い方の紹介も含めて、改めて新しいカブロボ DwRobot03 を作ってみましょう。Eclipse を使って、今までと同様の方法で新たに DwRobot03 というクラスをプロジェクトに新規追加します:
パッケージエクスプローラには新たに DwRobot03.java というファイルが追加されたはずなので、このファイルを開いて、内容を以下のように編集します。今回は order メソッドだけでなく、screening メソッドにも処理を追加していますが、実際には今まで order メソッドだけで行っていた選別と発注という2つの処理を、2つのメソッドに分けて記述しています。この点だけでも少しずつ本格的なロボットになりつつあります。
DwRobot03.java の内容
import java.util.ArrayList;
import jp.tradesc.superkaburobo.sdk.driver.RobotDriver;
import jp.tradesc.superkaburobo.sdk.robot.AbstractRobot;
import jp.tradesc.superkaburobo.sdk.trade.EnumAnalysisSpan;
import jp.tradesc.superkaburobo.sdk.trade.TradeAgent;
import jp.tradesc.superkaburobo.sdk.trade.analysis.technicalindex.GoldenCross;
import jp.tradesc.superkaburobo.sdk.trade.data.Portfolio;
import jp.tradesc.superkaburobo.sdk.trade.data.Stock;
public class DwRobot03 extends AbstractRobot {
@Override
public void order(TradeAgent arg0) {
// TODO Auto-generated method stub
//. スクリーニング時に設定した売買リストのメモを取り出す (8)
DwMemo memo = ( DwMemo )arg0.getMemoManager().getObjectMemo();
if( memo != null ){
ArrayList<Stock> buyList = memo.getBuyList();
ArrayList<Stock> sellList = memo.getSellList();
//. 手持ちのポジションを清算するため、ポートフォリオを取得する (9)
ArrayList<Portfolio> portfolioList = arg0.getPortfolioManager().getPortfolio();
//. 売り候補リストと現在のポートフォリオとを比較する (10)
for( Portfolio portfolio : portfolioList ){
for( Stock stock : sellList ){
//. ポートフォリオ内の株式オブジェクトと、売り注文リスト内の株式オブジェクトを比較する (11)
if( portfolio.getStock().equals( stock ) ){
//. 株式コードが一致しているものがあったら、その銘柄は決済する
portfolio.orderReverseNowMarketAll();
}
}
}
//. 買い候補リストの銘柄には単元株数分だけ買い注文を発行する (12)
for( Stock stock : buyList ){
//. 取り出した銘柄の単元数を取得する
Integer unit = stock.getUnit();
//. 最低取引株数(=単元数)分だけ売買する
arg0.getOrderManager().orderActualNowMarket( stock, unit );
}
}
//. 全ての注文が終わったらメモを初期化する (13)
arg0.getMemoManager().setObjectMemo( null );
}
@Override
public void screening(TradeAgent arg0) {
// TODO Auto-generated method stub
//. 売買の候補リストを用意 (1)
ArrayList<Stock> buyList = new ArrayList<Stock>(); //. 買い候補
ArrayList<Stock> sellList = new ArrayList<Stock>(); //. 売り候補
//. ゴールデンクロスの有無をチェックするためのクラスを用意する (2)
GoldenCross gc = arg0.getAnalysisManager().getGoldenCross( EnumAnalysisSpan.DAILY, 13, 34 );
//. このシミュレーションで取引できる全株銘柄を取得する
ArrayList<Stock> stockList = arg0.getInformationManager().getStockList();
//. 個別に銘柄を1つずつ取得して、ゴールデンクロス・デッドクロスの有無をチェックする
for( Stock stock : stockList ){
//. 取り出した銘柄のゴールデンクロス/デッドクロス発生有無を調べる
Double x = gc.getIndexGoldenCross( stock );
if( x == 1.0 ){
//. ゴールデンクロスが発生していた場合は、買い注文の候補としてリストに追加する (3)
buyList.add( stock );
}else if( x == -1.0 ){
//. デッドクロスが発生していた場合は、売り注文の候補としてリストに追加する (4)
sellList.add( stock );
}
}
//. 買い/売りの候補リストを記憶するメモクラスを用意する (5)
DwMemo memo = new DwMemo();
//. メモに売買それぞれの候補リストを設定する (6)
memo.setBuyList( buyList );
memo.setSellList( sellList );
//. メモを格納する (7)
arg0.getMemoManager().setObjectMemo( memo );
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] arg = { "-n", "DwRobot03" };
RobotDriver.main( arg );
}
}
|
では上記プログラムの解説を行います。まずは screening メソッド内から参照してください。このメソッドは後場の終わった毎営業日の夜に1度だけ実行されます。ザラ場は終わっていますので、この中では注文処理は発行できません。その代わり、いわゆる「スクリーニング」という、発注前の銘柄選別の処理を済ませておくことができます。今回作成する DwRobot03 では翌営業日の前場に備えて、上昇しそうな銘柄と下落しそうな銘柄をあらかじめ抽出しておくことにします。またここでの「上昇しそう」とか「下落しそう」という判断には前回紹介した DwRobot02 と同様にゴールデンクロス/デッドクロスを用いることにします。
screening メソッドではまず上昇および下落銘柄を記憶しておく変数をそれぞれ用意します(1)。Java では ArrayList<クラス名> という記述で、指定したクラスの配列を意味します。上記の (1) では個別銘柄を意味する Stock 型の配列変数を2つ定義して、それぞれ buyList(買いの候補)、sellList(売りの候補)という名前を付けておきました。この時点では単に変数を定義しただけで、まだ中身はありません。
次にゴールデンクロス/デッドクロスを判断するための変数 gc を定義します(2)。この部分は DwRobot02 での処理とほぼ同じですが、今回は半場単位ではなく、日足単位のデータでゴールデンクロス/デッドクロスを判断しますので、データを扱う単位に EnumAnalysisSpan.DAILY を指定している点が異なります。
この後は DwRobot02 での処理とほぼ同じで、個別銘柄ごとにゴールデンクロス/デッドクロスの発生有無を調べます。ただ今回のロジックでは「ゴールデンクロスを見つけたら買う」だけでなく、「デッドクロスを見つけたら売る(決済する)」というロジックも必要になります。また screening メソッド内での注文処理はできません。そこで (1) で用意した配列変数を使って、「ゴールデンクロスを見つけたら、その銘柄を買いの候補に追加」します(3)。また同様に「デッドクロスを見つけたら、その銘柄を売りの候補に追加」します(4)。なお、ゴールデンクロスもデッドクロスも発生していない銘柄に対しては何も行いません。この内容を全ての銘柄に対して行います。
全銘柄に対するゴールデンクロス/デッドクロスの調査が終わったら、このリストを翌営業日の発注する時まで記憶しておく必要があります。このまま screening メソッドが終了してしまうと、せっかく調べた buyList も sellList も消えてしまいます。カブロボではオブジェクトメモという機能を利用して、データの保存と取り出しを行うことができるようになっています。実はこのオブジェクトメモというのが、上記で作った DwMemo になります。
従って、まずはこの DwMemo を新規に作成します(5)。そして作成した buyList と sellList をメモのそれぞれ上昇候補/下落候補として設定します(6)。最後にこのメモを格納して(7) screening メソッドを終了します。この方法で格納したメモの内容は次回に上書きするまでの間はずっと保存されます。
次に発注を行う order メソッドに注目しましょう。まずは screening 処理の直後、つまり翌営業日の午前場の注文を行うべく、order メソッドが実行されます。まず DwMemo を取り出します(8)。このメモには前営業日に screening メソッドが調べた買いと売りの候補が格納されているはずですが、カブロボが起動して一番最初の営業日の朝の場合は前営業日の screening メソッドは動作していないことになります。またそれ以外の日でも screening の処理中に何らかのエラーが発生して、DwMemo が正しく格納されていない可能性もあります。したがって DwMemo が null(空)でないことを確認した上で、メモから上昇候補の銘柄リスト buyList と下落候補の銘柄リスト sellList を取り出します。またこのタイミングで現在のポートフォリオも取り出しておきます(9)。
ここまでができれば、後は発注を行うだけです。具体的には現在所有しているポートフォリオと下落候補の銘柄リストを比較して、ポートフォリオの中に下落候補が見つかったら決済します(なければそのままホールドします)。 まずはポートフォリオから1つずつ銘柄を取り出し、また下落リストからも各銘柄を取り出して(10)、これらの株式オブジェクトが一致しているかどうかを調べることで、ポートフォリオ内の銘柄が下落リストに含まれているかどうかを調査します(11)。一致していた場合、つまりポートフォリオ内の銘柄にデッドクロスが発生して下落候補と判断されていた場合は、そのポートフォリオを全決済します。
また上昇候補の銘柄に対してはリストから1つずつ銘柄を取り出しては(12)、今までと同様に単元株数だけ買いの注文を行います。これが午前場の order メソッド内での注文処理になります。
オブジェクトメモから取り出した銘柄に対する処理が全て完了したら、最後にオブジェクトメモを空のデータで上書きします(13)。こうしておくことで次に(午後場に)order メソッドが実行された時に取り出したオブジェクトメモは空になるので、2重注文を避けることができるようになります(戦略としては疑問ですが、午後場にも午前場と全く同じ注文を繰り返したい、というのであれば (13) は不要です)。
ついに利益が!?
これが DwRobot03 の全てです。main メソッド内で指定するロボット名を自分自身である "DwRobot03" に指定するのを忘れないでください。ではこのロボットを起動して1年間のシミュレーションを行ってみましょう。以下のような最終結果が出力されるはずです:
DwRobot03 が停止した後の Console の内容
■■最終成績表■■■■■■■■■■■■■■■■■■■■
--●取引データ●--------------------------------------
初期資産額(円) : 50,000,000
最終資産額(円) : 51,529,401
取引開始日 :2004-09-01
取引終了日 :2005-08-31
経過日数(日) :365
運用日数(日) :245
総トレード数 :211
勝ちトレード数 :102
負けトレード数 :109
勝率(%) :48.34
年間平均トレード数 :159
全トレード平均期間(日) :45
勝ちトレード平均期間(日) :62
負けトレード平均期間(日) :29
最長フラット期間(日) :1
トータル約定金額(円) :232,976,000
--●損益データ●--------------------------------------
トータル純損益(%) :3.06
勝ちトレード純利益(%) :7.7
負けトレード純損失(%) :-4.22
買いトレード純損益(%) :3.48
売りトレード純損益(%) :0
平均損益(%) :2.44
平均利益(%) :8.45
平均損失(%) :-3.17
年率換算利回り(%) :3.06
最大勝ちトレード(%) :51.99
最大負けトレード(%) :-9.48
--●指標データ●--------------------------------------
平均ドローダウン(%) :1.31
最大ドローダウン(%) :2.8
損益レシオ(倍) :1.95
プロフィットファクター(倍):1.82
リスクレシオ(倍) :1.09
年率シャープレシオ(倍) :0.82
年率ボラティリティ(%) :3.75
■■■■■■■■■■■■■■■■■■■■■■■■■■■
|
・・・なんというか、やっと少しだけ安心できるカブロボができあがってくれたようです(苦笑)。一年間で 211 回のトレードを繰り返し、勝率は 50% 弱でしたが、純損益は 3.06% の増加になりました。最大ドローダウンも 2.8% とかなり低く、ボラティリティも年率 3.75% とかなり安定したリスクの低い運用をしてくれたことがわかります。定期預金よりはお勧めできるかも。
次回はリスク管理
本連載記事の4回目ではじめて新規注文および決済という両方のタイミングに考慮したロボットの作成までこぎつけることができました。しかも(偶然ですが)ある程度の結果もついてきてくれたことで、投資ロボットとして1つの完成形に近いものができたのでは、という印象です。
次回紹介予定のカブロボでは、この DwRobot03 をベースとした応用の1つとして、リスク管理の考え方を実装する予定です。機械的にゴールデンクロスで買ったものを、再度機械的にデッドクロスで売る、という方法は単純で、ある意味理解しやすいのは確かです。ただこの決済のタイミングをより柔軟に、リスクとにらめっこしながら行う、というロボットに進化させてみます。
著者について  | |  | 木村 桂: 日本IBM ソフトウェア事業 |
記事の評価
|