レベル: 中級 木村 桂 (kimuc@jp.ibm.com), ソフトウェア事業, IBM
2007年 4月 20日
前回までの紹介でゴールデンクロス・デッドクロスにあわせた売買や、ポジション管理の手法を紹介しましたが、今回は空売りに挑戦します。残念ながら(ある意味、健全なことではありますが)株式相場は右肩上がりとは限らず、全般的なトレンドが下落基調になる時もあります。そんな中での投資で利益を得ようとすると、全般的には下落的な中でも成長する銘柄を見つけ出すか、或いは空売りのポジションを取って下落を利益に結びつける必要があります。前者が分かれば苦労はしませんので、今回は後者を紹介します。
予習編の第6回目となりました。本来はこれが最終回の予定でしたが予定を変更して、次回を最終回とさせていただきます。ただしロボット作成の紹介という意味では今回が最終回となります。
ところで「空売り」って?
ところで「空売り」ってどういうものか御存知でしょうか?分かっている方は次のセクションをご覧いただいて構いませんが、ここで簡単に説明しておきます。勘違いしてはいけないのは「株価が下がっても損しない魔法」ではない、ということです。各種指標などからある銘柄の株価がこの後に下落する、と判断したと仮定します。もしこの銘柄の株を所有している場合はここで売って決済することになるでしょう。
問題は株を持っていない場合です。通常、株は「安く買って(その後に)高く売る」ことで、この価格差を利益としますが、この順序を逆にします。つまり「高く売って(その後に)安く買う」という場合も順序が逆になっているだけで利益が生じることになります。正確には最初に売る株を借りて、その株を売ります。この状態では株を借りている状態です。次に株が安くなってから(借りたぶんを返すために)その株を買って、株を返します。
これが空売りと呼ばれる手法です。順序が違うので違和感があるかもしれませんが「安い時に買って、高い時に売る」という点では通常の取引と同じことをしています。ただ全く同じというわけではありません。実際には証券会社など個別のケースによりますが、以下のような点が異なります:
- 手数料が異なるケースが多いです
- 新規注文(売り)から決済(買い)までの期間に制限がある場合が多いです
- アップティックルール※が適用されることがあります
※直前の値段よりも高い値段でしか売り注文が執行されない、というルール。カブロボでも適用されます。
これが空売りです。手数料等の面で通常の(空売りでない)売買と比べると必ずしも対等な条件ではありませんが、株価が下落基調にある場合にリスクを負いながらも、積極的にリターンを求める手法といえます。ただし通常の売買と同様、大事なのはリスク管理であり、下落しているからといって空売りすれば利益になる、という単純なものではありません。条件が異なるため、空売りには空売りのリスク管理が求められる点に注意が必要になります。
空売りに挑戦!
ではこの空売りのロジックをカブロボにも実装してみましょう。今回作成するロボットは空売りという特殊な性格を持つこともあって、今までの動作からは少し変更して、以下のようなロジックを実装することとします:
- 各銘柄ごとにゴールデンクロスが発生したら買い、デッドクロスが発生したら空売りする。
-
買った銘柄は価格が 15% 上昇したら利益確定、または 10% 下落したら損切で、それぞれ決済する。
-
空売りした銘柄は価格が 10% 下落したら利益確定、まあは 7% 上昇したら損切で、それぞれ決済する。
今まではゴールデンクロスで買った銘柄をポジション管理すると同時に、デッドクロス時にも決済していました。今回はこの点を少し変更して、決済は価格がある程度上下した時だけにしています。そしてデッドクロス時には積極的に空売りを行うことにしました。空売りした銘柄の決済も価格の上下幅で判断することにします。
過去の記事と同様、今回も新しいロボット: DwRobot05
を作成しますので、過去の記事を参考に新規にロボットを作成してください。そして、以下のコードを入力します。
DwRobot05.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 DwRobot05 extends AbstractRobot {
@Override
public void order(TradeAgent arg0) {
// TODO Auto-generated method stub
//. 手持ちのポジションを清算するため、ポートフォリオを取得する
ArrayList<Portfolio> portfolioList = arg0.getPortfolioManager().getPortfolio();
//. 売り候補リストと現在のポートフォリオとを比較する
for( Portfolio portfolio : portfolioList ){
//. 購入時の約定値段
Integer execPrice = portfolio.getExecPrice();
//. 購入時の枚数 (1)
Integer execQty = portfolio.getExecQty();
//. この銘柄の現在の価格
Stock stock = portfolio.getStock();
Integer closingPrice
= arg0.getInformationManager().getStockSession( stock ).getClosingPrice();
//. ポジションの種類(買い/空売り)ごとにポジション管理の方法を変える (2)
if( execQty > 0 ){
//. 買いポジションの場合 (3)
if( closingPrice >= execPrice * 1.15 ){
//. 既に購入価格よりも 15% 上昇していたら、利益確定で決済 (4)
portfolio.orderReverseNowMarketAll();
}else if( closingPrice <= execPrice * 0.9 ){
//. 既に購入価格よりも 10% 下落していたら、損切で決済 (5)
portfolio.orderReverseNowMarketAll();
}
}else{
//. 空売りポジションの場合 (6)
if( closingPrice <= execPrice * 0.9 ){
//. 既に購入価格よりも 10% 下落していたら、利益確定で決済 (7)
portfolio.orderReverseNowMarketAll();
}else if( closingPrice <= execPrice * 1.07 ){
//. 既に購入価格よりも 7% 上昇していたら、損切で決済 (8)
portfolio.orderReverseNowMarketAll();
}
}
}
//. スクリーニング時に設定した売買リストのメモを取り出す
DwMemo memo = ( DwMemo )arg0.getMemoManager().getObjectMemo();
if( memo != null ){
ArrayList<Stock> buyList = memo.getBuyList();
ArrayList<Stock> sellList = memo.getSellList();
//. 買い候補リストの銘柄には単元株数分だけ買い注文を発行する
for( Stock stock : buyList ){
//. 取り出した銘柄の単元数を取得する
Integer unit = stock.getUnit();
//. 最低取引株数(=単元数)分だけ購入する
arg0.getOrderManager().orderActualNowMarket( stock, unit );
}
//. 売り候補リストの銘柄には単元株数分だけ空売り注文を発行する (9)
for( Stock stock : sellList ){
//. 取り出した銘柄の単元数を取得する (10)
Integer unit = stock.getUnit();
//. この銘柄の現在の価格 (11)
Integer closingPrice
= arg0.getInformationManager().getStockSession( stock ).getClosingPrice();
//. この銘柄の呼び値 (12)
Integer spread
= arg0.getInformationManager().checkSpread( closingPrice );
//. 最低取引株数(=単元数)分だけ空売りする (13)
arg0.getOrderManager().orderCreditNowLimit( stock, closingPrice + spread, -1 * 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", "DwRobot05" };
RobotDriver.main( arg );
}
}
|
screening メソッドは過去に作成した DwRobot03 や DwRobot04
のものと同じです。screening ではゴールデンクロスやデッドクロスの有無だけを判断しており、この点は
DwRobot05 でも変更ありません。 また main
メソッドもロボット名を指定する箇所以外は変更ありません。
一方、order メソッドは大きく変更しているように見えます。実際には DwRobot04
のコードの多くを流用しているのですが、実行の順序が変わっていたりしていますので注意してください。基本的にはまずポートフォリオの各銘柄ごとにポジション管理を行って必要であれば決済注文を執行し、その後に
screening
で選別した銘柄の新規注文を執行する、という流れになります。特に今回新しく利用する機能や考え方の部分だけを赤字で指定しています。
まず以前のロボットと同様にポートフォリオの処理を行うため、ポートフォリオのリストを取得し、1つずつ取り出します。DwRobot04
では各ポートフォリオのポジション管理をするため約定時の値段と現在の価格(直近の終値)を取得していましたが、今回の
DwRobot05 ではこれらに加えて購入時の枚数を取得します(1)。DwRobot05
では空売りを行いますが、空売り時には空売りを行った枚数に -1 をかけた数が得られます(つまり 100
枚空売りした場合、(1) の結果は -100 になります)。そこでまずは (1)
の結果が正の数か負の数かを調べて、購入のポジションか空売りのポジションかを判断します(2)。そして値が正の数で買いポジションであったと判断された場合(3)は、前回の
DwRobot04 の時と同様に 15% の値上がりで利益確定するか(4)、10%
の下落で損切の決済を行う(5)よう指示します。一方値が負の数で売りポジションであったと判断された場合(6)の条件は少し変更して、10%
の下落で利益確定するか(7)、7% の上昇で損切の決済を行う(8)ようなロジックとします。
また、ポートフォリオの処理が終了した後はスクリーニングの結果をオブジェクトメモから取り出します。そして上昇のサインがあったと判断された銘柄については以前と同様に単元株数だけ買いの注文を行います。また下落のサインがあったと判断された銘柄に対しては、これらを1つずつ取り出し(9)、それぞれの単元数(10)に加えて、現在の価格を求めます(11)。これはスーパー・カブロボ・コンテストにおいては空売り時にアップティックルールが適用されるため、注文時には現在の価格を知っておく必要があるためです。またこの銘柄の「呼び値」も取得しておきます(12)。そして現在の価格から呼び値だけ高い値段を指定して単元株数分の空売り注文を執行します(13)。これがスーパー・カブロボでの空売りの注文方法になります。この注文方法だけをとっても、空売り注文は通常の買い注文と比較してやや不利と考える必要があります。
全ての処理が終わったらオブジェクトメモを初期化して注文が全て終了となります。 これが予習編で作成するカブロボの最終形になります。
空売りロボット実行時の注意
ではこの DwRobot05 を実行してみましょう! と、その前に1つ注意点があります。カブロボ SDK
では作成したロボットに空売りを許可するかどうかを robot-config.xml
内で設定することができるようになっています。プロジェクト内の robot-config.xml
をダブルクリックして開くと、下から 11 行目に
<short-trade>true</short-trade>
|
と書かれた行が見つかります。このタグで括られた値が true の場合は空売りを許可、false
の場合は空売りを許可しないという設定になります。したがってこの設定の場合は空売りを許可していることになります。
なお、この設定で空売りを許可しない(<short-trade>false</short-trade>)場合、DwRobot05
のように空売りの命令が実行されても全て無条件に失敗する(注文が成立しない)ようになっています。
では改めてこのロボットを実行してみます(空売りは許可されています)。実行すると、以下のような最終結果になります:
DwRobot05 が停止した後の Console の内容
■■最終成績表■■■■■■■■■■■■■■■■■■■■
--●取引データ●--------------------------------------
初期資産額(円) : 50,000,000
最終資産額(円) : 53,736,926
取引開始日 :2004-09-01
取引終了日 :2005-08-31
経過日数(日) :365
運用日数(日) :245
総トレード数 :260
勝ちトレード数 :166
負けトレード数 :94
勝率(%) :63.85
年間平均トレード数 :194
全トレード平均期間(日) :87
勝ちトレード平均期間(日) :116
負けトレード平均期間(日) :37
最長フラット期間(日) :1
トータル約定金額(円) :223,715,200
--●損益データ●--------------------------------------
トータル純損益(%) :7.47
勝ちトレード純利益(%) :12.32
負けトレード純損失(%) :-4.45
買いトレード純損益(%) :7.95
売りトレード純損益(%) :-0.09
平均損益(%) :4.44
平均利益(%) :9.3
平均損失(%) :-4.14
年率換算利回り(%) :7.47
最大勝ちトレード(%) :22.71
最大負けトレード(%) :-11.54
--●指標データ●--------------------------------------
平均ドローダウン(%) :1.78
最大ドローダウン(%) :6.06
損益レシオ(倍) :1.57
プロフィットファクター(倍):2.77
リスクレシオ(倍) :1.23
年率シャープレシオ(倍) :1.08
年率ボラティリティ(%) :6.9
■■■■■■■■■■■■■■■■■■■■■■■■■■■
|
以前までの最終結果と異なるのは売りトレードに関する純損益データ(-0.09%)が集計されていることです。過去の予習編では空売りを行っていなかったため、最終結果でも全て
0
でしたが、このロボットでは空売りを行っているためデータが得られた結果です。またこのアクティブ運用を行った結果、
7.47% の純損益を記録しました。
なお、DwRobot05.java の内容はそのままに、上記の robot-config.xml
内の空売り設定を
<short-trade>false</short-trade>
|
と変更して空売りを禁止した場合(つまりゴールデンクロスで買って、15%上昇か10%下落で決済するだけの場合)、同じロボットを同じ期間で実行すると最終結果は以下のように(わずかですが)変わります:
■■最終成績表■■■■■■■■■■■■■■■■■■■■
--●取引データ●--------------------------------------
初期資産額(円) : 50,000,000
最終資産額(円) : 53,945,240
取引開始日 :2004-09-01
取引終了日 :2005-08-31
経過日数(日) :365
運用日数(日) :245
総トレード数 :165
勝ちトレード数 :127
負けトレード数 :38
勝率(%) :76.97
年間平均トレード数 :148
全トレード平均期間(日) :138
勝ちトレード平均期間(日) :153
負けトレード平均期間(日) :90
最長フラット期間(日) :1
トータル約定金額(円) :152,370,600
--●損益データ●--------------------------------------
トータル純損益(%) :7.89
勝ちトレード純利益(%) :12.26
負けトレード純損失(%) :-4.13
買いトレード純損益(%) :8.13
売りトレード純損益(%) :0
平均損益(%) :7.15
平均利益(%) :12.07
平均損失(%) :-9.3
年率換算利回り(%) :7.89
最大勝ちトレード(%) :22.71
最大負けトレード(%) :-11.54
--●指標データ●--------------------------------------
平均ドローダウン(%) :1.71
最大ドローダウン(%) :6.02
損益レシオ(倍) :0.89
プロフィットファクター(倍):2.97
リスクレシオ(倍) :1.31
年率シャープレシオ(倍) :1.14
年率ボラティリティ(%) :6.91
■■■■■■■■■■■■■■■■■■■■■■■■■■■
|
まとめ
以上、ここまで6回に渡ってスーパー・カブロボ・コンテスト向けのロボットを Java
言語でプログラミングする方法を紹介してきました。結果的にそこそこの実力と、一通りの機能を兼ね備えたカブロボが出来上がりました。参加を希望される皆さんは、これらのロボットを改良して日数を調整したり、別の(お気に入りの)テクニカル分析を使ってみたり、全く新しい発想のロジックに挑戦してみたり・・・ と、(全く手付かずのそのまんまはちょっと・・・ですが)なんでもいいので是非コンテストに参加していただけると作者冥利につきます。
まだまだ不充分な点・物足りない点などあるかもしれませんが、是非ご意見として賜りたいと思っています。この IBM
developerWorks
には他にカブロボの達人へのインタビュー記事やより上級の技術を紹介する記事(予定)も用意されますので、これらもあわせて参照ください。
基本的なロボット紹介は今回が最後ですが、次回は作成したロボットをコンテストに登録するまでの手順を紹介して中級編の最終回とさせていただく予定です。
著者について  | |  | 木村 桂: 日本IBM ソフトウェア事業 |
記事の評価
|