レベル: 中級 木村 桂 (kimuc@jp.ibm.com), ソフトウェア事業, IBM
2007年 4月 13日
前回(第4回)の紹介では、オブジェクトメモを用いて日をまたいだデータの記憶や取り出し機能を実装し、この機能を利用した「ゴールデンクロス買い&デッドクロス売り」のカブロボを作りました。このゴールデンクロス/デッドクロスという考え方は、いわゆる「ダマし」など万能ではありませんが、シンプルでありながらもそれなりに利にかなった投資ロジックといえます。
しかし、このシンプルさの裏返しには「融通が利かない」という危険性が隠れています。この投資ロジックで運用する限り、何が起ころうともゴールデンクロスにならないと買いませんし、何が起ころうともデッドクロスにならないと売りません。株式投資の現実問題として、いわゆる「ストップ安」になると売りたくても売れなくなります。デッドクロスとストップ安が重なってしまうと、デッドクロスになって売りの命令が出ますが結果として売れないことになります。こうなってしまうと、この銘柄を売却できるのは、次のゴールデンクロスを1度経験した後の、その次のデッドクロス時、ということになってしまいます。例外的なケースとはいえ、日本の株式市場ではありえないというほどのレアケースでもありません。極端にシステマティックな投資ロジックにはこのような現実への対処という点で大きな弱点を抱えていることになります。
そこで大事になるのが「リスク管理」です。極端な言い方をすれば、どの銘柄をいつ買ったとしても、その段階では大きなリスクはありません。問題は「買った銘柄をどのように決済するか」です。利益だけを求める投資が理想ですが、現実にはそうはいかないこともありますので、「どのレベルで損切を行うか」といったリスク管理を実装するロボットの作り方を今回のテーマとしてみます。
自分の身は自分で守ろう! ではなく、守らせよう!!
過去の例と同様にして、新しく DwRobot04 を Eclipse のプロジェクトに追加します。
作成した DwRobot04.java の内容を以下のように変更します。基本線は DwRobot03.java
と同じなので、DwRobot03.java
からのコピー&ペーストを元に、(以下に赤字で示した)異なる部分だけ変更すると編集作業が比較的楽になると思います。
DwRobot04.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 DwRobot04 extends AbstractRobot {
@Override
public void order(TradeAgent arg0) {
// TODO Auto-generated method stub
//. スクリーニング時に設定した売買リストのメモを取り出す
DwMemo memo = ( DwMemo )arg0.getMemoManager().getObjectMemo();
if( memo != null ){
ArrayList<Stock> buyList = memo.getBuyList();
ArrayList<Stock> sellList = memo.getSellList();
//. 手持ちのポジションを清算するため、ポートフォリオを取得する
ArrayList<Portfolio> portfolioList = arg0.getPortfolioManager().getPortfolio();
//. 売り候補リストと現在のポートフォリオとを比較する
for( Portfolio portfolio : portfolioList ){
//. ポートフォリオ銘柄が下落リストに含まれていたかどうかを判断するための変数 (1)
boolean notInSellList = true;
for( Stock stock : sellList ){
//. ポートフォリオ内の株式オブジェクトと、売り注文リスト内の株式オブジェクトを比較する
if( portfolio.getStock().equals( stock ) ){
//. 一致した (2)
notInSellList = false;
//. 株式コードが一致しているものがあったら、その銘柄は決済する
portfolio.orderReverseNowMarketAll();
}
}
//. デッドクロス発生前でも、ポジションが特定条件を満たしていれば決済する (3)
if( notInSellList ){
//. ポートフォリオ銘柄が下落リストに含まれていなかった場合はポジション管理を行う (4)
//. 購入時の約定値段 (5)
Integer execPrice = portfolio.getExecPrice();
//. この銘柄の現在の価格 (6)
Stock stock = portfolio.getStock();
Integer closingPrice
= arg0.getInformationManager().getStockSession( stock ).getClosingPrice();
if( closingPrice >= execPrice * 1.15 ){
//. 既に購入価格よりも 15% 上昇していたら、利益確定で決済 (7)
portfolio.orderReverseNowMarketAll();
}else if( closingPrice <= execPrice * 0.9 ){
//. 既に購入価格よりも 10% 下落していたら、損切で決済 (8)
portfolio.orderReverseNowMarketAll();
}
}
}
//. 買い候補リストの銘柄には単元株数分だけ買い注文を発行する
for( Stock stock : buyList ){
//. 取り出した銘柄の単元数を取得する
Integer unit = stock.getUnit();
//. 最低取引株数(=単元数)分だけ売買する
arg0.getOrderManager().orderActualNowMarket( stock, unit );
}
}
//. 全ての注文が終わったらメモを初期化する
arg0.getMemoManager().setObjectMemo( null );
}
@Override
public void screening(TradeAgent arg0) {
// TODO Auto-generated method stub
//. 売買の候補リストを用意
ArrayList<Stock> buyList = new ArrayList<Stock>(); //. 買い候補
ArrayList<Stock> sellList = new ArrayList<Stock>(); //. 売り候補
//. ゴールデンクロスの有無をチェックするためのクラスを用意する
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 ){
//. ゴールデンクロスが発生していた場合は、買い注文の候補としてリストに追加する
buyList.add( stock );
}else if( x == -1.0 ){
//. デッドクロスが発生していた場合は、売り注文の候補としてリストに追加する
sellList.add( stock );
}
}
//. 買い/売りの候補リストを記憶するメモクラスを用意する
DwMemo memo = new DwMemo();
//. メモに売買それぞれの候補リストを設定する
memo.setBuyList( buyList );
memo.setSellList( sellList );
//. メモを格納する
arg0.getMemoManager().setObjectMemo( memo );
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] arg = { "-n", "DwRobot04" };
RobotDriver.main( arg );
}
}
|
まず screening メソッドの内容は DwRobot03
から一切変更ありません。つまりゴールデンクロス/デッドクロスを判断材料に買い銘柄と売り銘柄を選別してオブジェクトメモに格納します。main
メソッドもロボット名が変更になっただけです。これらは今回特に解説を加えません。またorder
メソッドですが、ここも基本的なロジックは DwRobot03
のものを流用しています。そこで今回新たに変更した赤字の部分を解説します(DwRobot03
から削除した行はありません。全て新規に追加した部分ばかりです)。
変更しているのは決済の部分だけです。オブジェクトメモから取り出した売り銘柄とポートフォリオの内容を比較する際に
notInSellList
という変数を新たに用意しています(1)。この変数はポートフォリオ内の各銘柄が売り銘柄に含まれていたかどうかを判断するための変数にします。初期値には
true
が設定されており、これは「売り銘柄に含まれていない」ことを意味しています。そして比較した結果、売り銘柄に含まれていた場合はこの変数の値を
false
に変更します(2)。これによって「売り銘柄に含まれていた」と判断できるようになります。そしてこの直後に成行きの決済命令を発行します。
したがって、ポートフォリオ内のある銘柄と売り銘柄リストとを比較し終えた後、notInSellList
の値が初期値である true のままであれば決済は行われておらず、逆に false
に変更になっていればこの銘柄は決済が行われた、と判断できます。そこでまだ決済が行われていないポートフォリオ銘柄だった場合は(3)、更に以下のポジション管理を行うことにします(4)。
ポジション管理のために、まずこの銘柄を購入した時の値段を取り出します(5)。具体的にはポートフォリオから
getExecPrice
というメソッドを実行することで取得できます。また比較のため、この銘柄の現在の価格(つまり直前の終値)も取り出します(6)。そしてこれらの値からポジションの判断をさせます。今回のカブロボでは直近の終値が購入価格の
1.15 倍以上になっていたら(つまり 15% 以上上昇していたら)利益確定(7)、直近の終値が購入価格の
0.90 倍以下になっていたら(つまり 10%
以上下落していたら)損切りさせる(8)こと、という判断にしました。この数値がふさわしいかどうかは分かりませんが、このようなルールを設けておくことで、より安全な投資スタイルでカブロボを動作させることができるようになります。
なお買い候補の銘柄の扱いについては DwRobot03
と同様で、特に変更はありません。また実際にはこのコードに加えて前回作成した DwMemo.java
をあわせて実行することになります。
リスク管理の効果は・・・?
では、このカブロボを起動してみます。今までの紹介内容と同様に、DwRobot04.java
を右クリックして Run As → Java Application を選択します。
すると DwRobot04 が起動し、Console
に途中経過が出力され、最終的には以下のような最終結果を出力して停止します。
DwRobot04 が停止した後の Console の内容
■■最終成績表■■■■■■■■■■■■■■■■■■■■
--●取引データ●--------------------------------------
初期資産額(円) : 50,000,000
最終資産額(円) : 51,420,899
取引開始日 :2004-09-01
取引終了日 :2005-08-31
経過日数(日) :365
運用日数(日) :245
総トレード数 :211
勝ちトレード数 :102
負けトレード数 :109
勝率(%) :48.34
年間平均トレード数 :159
全トレード平均期間(日) :41
勝ちトレード平均期間(日) :54
負けトレード平均期間(日) :29
最長フラット期間(日) :4
トータル約定金額(円) :232,879,100
--●損益データ●--------------------------------------
トータル純損益(%) :2.84
勝ちトレード純利益(%) :7.52
負けトレード純損失(%) :-4.23
買いトレード純損益(%) :3.28
売りトレード純損益(%) :0
平均損益(%) :1.95
平均利益(%) :7.45
平均損失(%) :-3.2
年率換算利回り(%) :2.84
最大勝ちトレード(%) :22.7
最大負けトレード(%) :-10.68
--●指標データ●--------------------------------------
平均ドローダウン(%) :1.2
最大ドローダウン(%) :2.8
損益レシオ(倍) :1.9
プロフィットファクター(倍):1.78
リスクレシオ(倍) :1.01
年率シャープレシオ(倍) :0.85
年率ボラティリティ(%) :3.34
■■■■■■■■■■■■■■■■■■■■■■■■■■■
|
この結果を見ると、トータル純損益はわずかに減って 2.84%
になりました。これはそれまでのロジックでは1回のトレードでより大きな利益を狙っていたのに対して、今回は利益の幅を決めて早めに利益確定させるようにした結果ともいえます。その証拠に年率ボラティリティは前回の結果から更に小さくなって
3.34% にまで減っていることが確認できます。上記ロジック内の 1.15 や 0.9
という数値を変更することでリスク管理のルールを変更することもできますので、興味のある方は是非ご自身でルールを変更してみて、その最終結果がどのように変わるのかを試してみてください。
次回は予習編の最終回
連載は5回目ですが、ここまでくるとかなり本格的な投資ロジックを搭載したロボットになってきました。次回はついに予習編の最終回となります。最後は今回紹介したような安定運用を目指す方向とは逆の、下落相場に備えた空売りも行うアクティブな運用方針を目指すカブロボを紹介します。
著者について  | |  | 木村 桂: 日本IBM ソフトウェア事業 |
記事の評価
|