 | レベル: 上級 木村 桂 (kimuc@jp.ibm.com), ソフトウェア事業, IBM
2006年 3月 31日 この連載も最終回である7回目を迎えました。前回はカブロボコンテストのルールによる制限をあえて無視して、長期間の投資シミュレーションを行うことを目的とする内容を紹介しました。今回はサイコロジカルラインと RSI という、2つの似た手法を紹介します。更にこれらを使って前回同様長期シミュレーションを行ってみます。
カブロボを実投資に応用
まずはこれら2つの手法を紹介します。
サイコロジカルライン(Psychological Line)
この手法は読んで字のごとく、投資家の「心理的な」状態を見極めた上で売買を行おうとする手法です。一般的に投資家の心理として上昇がある程度続くと「そろそろ下がるかな」と弱気に考えがちになります。また下落が続くと今度は「そろそろ上がる頃かも」と都合のよい方向に考えるようになります。このような心理的なゆらぎを客観的に数字で表そうというのがサイロコジカルラインです。
サイコロジカルラインは通常12日前後の固定日数間において、前営業日の終値よりも終値が上昇した日の割合がどれだけあるか、という数値を百分率で表します。その数値が 25% を下回っている場合は「売られすぎであり、そろそろ上昇する」レベルにあると考えます。逆に 75% を上回っている場合は「買われすぎであり、そろそろ下落する」レベルであると分析します。
RSI(Relative Strength Index)
RSI は名前が RCI に似ていて、また同じような動きをすることもあるのですが、実際には上記のサイコロジカルラインと似た考え方です。サイコロジカルラインの考え方に更に上昇/下落の値幅という概念を加えます。具体的には終値を上昇した日数をカウントするのではなく、上昇した幅の合計を計算します。また同じ期間の下落幅も計算して、変化の合計幅を求め、その中で上昇幅の占める割合を百分率で求めるようにしたものが RSI になります。
(例 ある銘柄の5日間のサイコロジカルラインと RSI の例)
| 日数 | 株価 | サイコロジカルライン | RSI |
|---|
| 上昇 | 下落 | 上昇幅 | 下落幅 |
|---|
|
| 11545 |
|
|
|
| | 5 | 11520 |
| ○ |
| 25 | | 4 | 11539 | ○ |
| 19 |
| | 3 | 11528 |
| ○ |
| 11 | | 2 | 11439 |
| ○ |
| 99 | | 1 | 11441 | ○ |
| 2 |
| |
| 計 | 2
|
3
|
21
|
135
|
|---|
サイコロジカルラインの値 = 2/5 = 0.4 = 40% RSI の値= 21 / ( 21 + 135 ) = 0.1316.. ≒ 13%
したがって、この例ではサイコロジカルラインでは値が 40% のため、特別に売られすぎ買われすぎのレベルとは判断できないが、RSI では 13% で 25% を下回っているため売られすぎである、という判断になります。
アルゴリズム化
サイコロジカルラインも RSI も 25% を割ったタイミングと 75% を超えたタイミングがカギになりますから、これらのタイミングに合わせて買い/売りの注文を出すようなプログラムを作ります。したがって、前日の値と今日の値の2つを計算し、
(1)昨日の時点では 25% 以上だった、かつ、今日の時点では 25% 以下だった →買い (2)昨日の時点では 75% 以下だった、かつ、今日の時点では 75% 以上だった →売り
というアルゴリズムをサイコロジカルラインと RSI の両方で実装します。
Java のプログラムで実装(サイコロジカルライン)
まずサイコロジカルラインで説明します。これまでに説明してきたアルゴリズムを実装すると以下のようになります。ここでは SystemTradePsychological というクラス名でロボットを作成しました:
import java.util.Calendar;
import java.util.List;
import jp.kaburobo.information.IndexInformation;
import jp.kaburobo.information.InformationManager;
import jp.kaburobo.investment.InvestmentAgent;
import jp.kaburobo.investment.SimpleStockOrder;
import jp.kaburobo.investment.Stock;
import jp.kaburobo.investment.StockOrder;
import jp.kaburobo.robot.Robot;
import jp.kaburobo.util.Time;
public class SystemTradePsychological implements Robot {
static int tradetype = -1;
public void run(InvestmentAgent arg0) {
// TODO Auto-generated method stub
int DAY = 9; // サイコロジカルライン値を求める日数 (0)
int IDX = 1; // (1:明治乳業, 2:日本ハム, 3:アサヒ, ...)(1)
Stock[] stocks = arg0.getStocks(); // 銘柄リストの取得
// 直近の日付から ( DAY + 1 ) 日分の銘柄指標データを取得
Calendar cdt = Time.getTime();
InformationManager infoManager = arg0.getInformationManager();
List list = infoManager.getIndexInformation( stocks[IDX], cdt, -1 * ( DAY + 1 + 1 ) );
long cp[] = new long[DAY+2];
for( int i = 0; i <= DAY + 1; i ++ ){
IndexInformation indexInformation = ( IndexInformation )list.get( i );
cp[i] = indexInformation.getClosingPrice(); // ( DAY + 1 - i )日前の終値
}
// 当日と前日のサイコロジカルライン値を求める (2)
double vY = getPsycho( cp, 0, DAY ); // 前日の値
double vT = getPsycho( cp, 1, DAY ); // 当日の値
System.out.println( stocks[IDX].getName() + "(" + stocks[IDX].getCode() + ")\tPSY: " + vT );
if( vY >= 0.25 & vT < 0.25 &tradetype != StockOrder.BUY ){
// 昨日から今日にかけて 25% を割った & 現在買いポジションではない (3)
// →売られすぎなので、買い
// 最初の売買だった場合は1単位、ドテンの場合は2単位
int quantity = ( tradetype == -1 ) ? 1 : 2;
tradetype = StockOrder.BUY;
System.out.println( "注文銘柄: " + stocks[IDX].getName() + "("
+ "(" + stocks[IDX].getCode() + ")\t終値: " + cp[DAY] );
SimpleStockOrder stockOrder = new SimpleStockOrder();
stockOrder.setStock( stocks[IDX] ); // 注文の銘柄を設定
stockOrder.setLimitType( StockOrder.MARKET ); // 成り行き注文
stockOrder.setTradeType( tradetype ); // 買い注文を設定
stockOrder.setQuantity( quantity ); // 注文を1単位株に設定
arg0.order( stockOrder ); // 注文の発行
}else if( vY <= 0.75 & vT > 0.75 & tradetype != StockOrder.SELL ){
// 昨日から今日にかけて 75% を超えた & 現在空売りポジションではない (4)
// →買われすぎなので、空売り
// 最初の売買だった場合は1単位、ドテンの場合は2単位
int quantity = ( tradetype == -1 ) ? 1 : 2;
tradetype = StockOrder.SELL;
System.out.println( "注文銘柄: " + stocks[IDX].getName() + "("
+ "(" + stocks[IDX].getCode() + ")\t終値: " + cp[DAY] );
SimpleStockOrder stockOrder = new SimpleStockOrder();
stockOrder.setStock( stocks[IDX] ); // 注文の銘柄を設定
stockOrder.setLimitType( StockOrder.MARKET ); // 成り行き注文
stockOrder.setTradeType( tradetype ); // 売り注文を設定
stockOrder.setQuantity( quantity ); // 注文を1単位株に設定
arg0.order( stockOrder ); // 注文の発行
}
}
//. サイコロジカルライン値 (2)
private double getPsycho( long[] v, int startidx, int num ){
int upcnt = 0;
for( int i = startidx; i < startidx + num; i ++ ){
if( v[i] < v[i+1] ){
// 上昇
upcnt ++;
}
}
return ( ( double )upcnt / num );
}
}
|
以下にこのプログラムの流れを簡単に紹介しますが、前回紹介した移動平均の例とほぼ同じ内容になっているので、異なる部分のみ紹介します。
(0) まずサイコロジカルラインを求めるための評価期間日数を決めます。通常は12日や14日という値を使いますが、あまり売買が少ないとつまらないのでここでは9日としています。
(1) 次に対象の銘柄を決定します。前回は 0 を指定して日本製粉株を対象としました。今回は 1 を指定して明治乳業株にします。もちろん皆さんはこの数値を 0 から 99 までの好きな数値を指定してかまいません。
(2) 計算に必要なだけの日数分の終値を求め、そこからサイコロジカルラインを求めます。サイコロジカルラインは価格が前日と比較して上昇しているかどうかを調べ、上昇していた日の数をカウントし、上昇した日が全体のどれだけの割合を占めるかを求めます。
(3) そして、前日から当日にかけて 25% を割り込んだのであれば買いのサインと判断します。ただ移動平均の時と少し事情が異なるのはこの買いのサインが出たあと、75% にまで戻って売りのサインがでる前に、もう一度 25% を割り込む、ということが考えられます。その場合にもう一度買いのサインを出してしまっては注文と決済のバランスが取れなくなってしまうので、「現在買いの注文を出していない状態で 25% を割り込んだ場合に買う」というルールにします。
(4) (3) と同様に「現在空売りの注文を出していない状態で 75% を超えた場合には売る」ようにします。
シミュレーション結果(サイコロジカルライン)
このプログラムを前回同様に 2005/01/01 から 2005/06/30 までの半年間シミューレートすると以下のような結果になります:
:
:
(略)
:
:
■28日目2005/01/28 金 終了時の所持金額
総資産 = 手持ち資金+所有株式時価総額 = 5,000,000円
手持ち資金(運転可能金額 + ロック金額) = 5,000,000円
運転可能金額 = 5,000,000円
ロック資金 = 0円
単純計算年率利益率 0.0%
○ロボットの銘柄評価
明乳(2261) PSY: 0.7777777777777778
注文銘柄: 明乳(2261) 終値: 615
売却: 2261 明乳 1単位 成行
【注文処理結果】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
明乳| 2261| -1000| 619| -14
【ポートフォリオ】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
明乳| 2261| -1000| 619| -14
実行時間: 0.047 秒.
:
:
(略)
:
:
■181日目2005/06/30 木 終了時の所持金額
総資産 = 手持ち資金+所有株式時価総額 = 4,985,000円
手持ち資金(運転可能金額 + ロック金額) = 5,619,000円
運転可能金額 = 4,351,000円
ロック資金 = 1,268,000円
単純計算年率利益率 0.0%
○ロボットの銘柄評価
明乳(2261) PSY: 0.6666666666666666
【ポートフォリオ】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
明乳| 2261| -1000| 619| 5
実行時間: 0.281 秒.
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
【最終結果】
あなたのロボットによる、該当期間の運用成績は、こちらです。
総資産 = 手持ち資金+所有株式時価総額 = 4,990,000円
単純計算年率利益率 0.0%
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
SystemTradePsychological end
|
結果としては 2005/01/28 に始めて 75% を超えたためそこで買い注文を行い、そのまま 25% を割り込むことが一度もなかったため、これが唯一の注文となりました。最終結果としては 10,000 円の損益となっています。
Java のプログラムで実装(RSI)
全く同じ日数・銘柄・ルールで、今度は RSI を用いてシステムトレーディングを行ってみます。サイコロジカルラインの場合と比べて本質的に異なっているのは、クラス名やコメント部分を除くと赤字の部分のみです:
import java.util.Calendar;
import java.util.List;
import jp.kaburobo.information.IndexInformation;
import jp.kaburobo.information.InformationManager;
import jp.kaburobo.investment.InvestmentAgent;
import jp.kaburobo.investment.SimpleStockOrder;
import jp.kaburobo.investment.Stock;
import jp.kaburobo.investment.StockOrder;
import jp.kaburobo.robot.Robot;
import jp.kaburobo.util.Time;
public class SystemTradeRsi implements Robot {
static int tradetype = -1;
public void run(InvestmentAgent arg0) {
// TODO Auto-generated method stub
int DAY = 9; // RSI 値を求める日数
int IDX = 1; // (1:明治乳業, 2:日本ハム, 3:アサヒ, ...)
Stock[] stocks = arg0.getStocks(); // 銘柄リストの取得
// 直近の日付から ( DAY + 1 ) 日分の銘柄指標データを取得
Calendar cdt = Time.getTime();
InformationManager infoManager = arg0.getInformationManager();
List list = infoManager.getIndexInformation( stocks[IDX], cdt, -1 * ( DAY + 1 + 1 ) );
long cp[] = new long[DAY+2];
for( int i = 0; i <= DAY + 1; i ++ ){
IndexInformation indexInformation = ( IndexInformation )list.get( i );
cp[i] = indexInformation.getClosingPrice();
}
// 当日と前日の RSI 値を求める
double vY = getRSI( cp, 0, DAY ); // 前日の値
double vT = getRSI( cp, 1, DAY ); // 当日の値
System.out.println( stocks[IDX].getName() + "(" + stocks[IDX].getCode() + ")\tRSI: " + vT );
if( vY >= 0.25 & vT < 0.25 & tradetype != StockOrder.BUY ){
// 昨日から今日にかけて 25% を割った & 現在買いポジションではない
// →売られすぎなので、買い
// 最初の売買だった場合は1単位、ドテンの場合は2単位
int quantity = ( tradetype == -1 ) ? 1 : 2;
tradetype = StockOrder.BUY;
System.out.println( "注文銘柄: " + stocks[IDX].getName() + "("
+ stocks[IDX].getCode() + ")\t終値: " + cp[DAY] );
SimpleStockOrder stockOrder = new SimpleStockOrder();
stockOrder.setStock( stocks[IDX] ); // 注文の銘柄を設定
stockOrder.setLimitType( StockOrder.MARKET ); // 成り行き注文
stockOrder.setTradeType( tradetype ); // 買い注文を設定
stockOrder.setQuantity( quantity ); // 注文を1単位株に設定
arg0.order( stockOrder ); // 注文の発行
}else if( vY <= 0.75 & vT > 0.75 & tradetype != StockOrder.SELL ){
// 昨日から今日にかけて 75% を超えた & 現在空売りポジションではない
// →買われすぎなので、空売り
// 最初の売買だった場合は1単位、ドテンの場合は2単位
int quantity = ( tradetype == -1 ) ? 1 : 2;
tradetype = StockOrder.SELL;
System.out.println( "注文銘柄: " + stocks[IDX].getName() + "("
+ stocks[IDX].getCode() + ")\t終値: " + cp[DAY] );
stockOrder.setStock( stocks[IDX] ); // 注文の銘柄を設定
stockOrder.setLimitType( StockOrder.MARKET ); // 成り行き注文
stockOrder.setTradeType( tradetype ); // 売り注文を設定
stockOrder.setQuantity( quantity ); // 注文を1単位株に設定
arg0.order( stockOrder ); // 注文の発行
}
}
// RSI値
private double getRSI( long[] v, int startidx, int num ){
double up = 0.0, down = 0.0;
for( int i = startidx; i < startidx + num; i ++ ){
if( v[i] < v[i+1] ){
// 上昇
up += ( double )( v[i+1] - v[i] );
}else{
// 下落
down += ( double )( v[i] - v[i+1] );
}
}
return ( up / ( up + down ) );
}
}
|
サイコロジカルラインでは上昇した日が何日あったかだけをカウントして、上昇日の割合だけを求めていましたが、RSI では上昇・下落の幅をカウントします。そしてこれらの合計である価格変化全体の中で上昇の変化がどの程度あったのか、を計算するようにしました。それ以外は全く同じです。
シミュレーション結果(RSI)
このプログラムも 2005/01/01 から 2005/06/30 までの半年間シミューレートすると以下のような結果になります:
:
:
(略)
:
:
■60日目2005/03/01 火 終了時の所持金額
総資産 = 手持ち資金+所有株式時価総額 = 5,000,000円
手持ち資金(運転可能金額 + ロック金額) = 5,000,000円
運転可能金額 = 5,000,000円
ロック資金 = 0円
単純計算年率利益率 0.0%
○ロボットの銘柄評価
日ハム(2282) RSI: 0.8181818181818182
注文銘柄: 日ハム(2282) 終値: 1450
売却: 2282 日ハム 1単位 成行
【注文処理結果】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
日ハム| 2282| -1000| 1,458| 6
【ポートフォリオ】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
日ハム| 2282| -1000| 1,458| 6
実行時間: 0.047 秒.
:
:
(略)
:
:
■84日目2005/03/25 金 終了時の所持金額
総資産 = 手持ち資金+所有株式時価総額 = 5,076,000円
手持ち資金(運転可能金額 + ロック金額) = 6,458,000円
運転可能金額 = 3,694,000円
ロック資金 = 2,764,000円
単純計算年率利益率 9.0%
○ロボットの銘柄評価
日ハム(2282) RSI: 0.2265625
注文銘柄: 日ハム(2282) 終値: 1384
購入: 2282 日ハム 2単位 成行
【注文処理結果】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
日ハム| 2282| 2000| 1,371| 6
【ポートフォリオ】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
日ハム| 2282| 1000| 1,371| 6
実行時間: 0.031 秒.
:
:
(略)
:
:
■101日目2005/04/11 月 終了時の所持金額
総資産 = 手持ち資金+所有株式時価総額 = 5,137,000円
手持ち資金(運転可能金額 + ロック金額) = 3,716,000円
運転可能金額 = 3,716,000円
ロック資金 = 0円
単純計算年率利益率 13.0%
○ロボットの銘柄評価
日ハム(2282) RSI: 0.7796610169491526
注文銘柄: 日ハム(2282) 終値: 1426
売却: 2282 日ハム 2単位 成行
【注文処理結果】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
日ハム| 2282| -2000| 1,412| 22
【ポートフォリオ】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
日ハム| 2282| -1000| 1,412| 22
実行時間: 0.047 秒.
:
:
(略)
:
:
■111日目2005/04/21 木 終了時の所持金額
総資産 = 手持ち資金+所有株式時価総額 = 5,212,000円
手持ち資金(運転可能金額 + ロック金額) = 6,540,000円
運転可能金額 = 3,884,000円
ロック資金 = 2,656,000円
単純計算年率利益率 16.0%
○ロボットの銘柄評価
日ハム(2282) RSI: 0.20481927710843373
注文銘柄: 日ハム(2282) 終値: 1338
購入: 2282 日ハム 2単位 成行
【注文処理結果】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
日ハム| 2282| 2000| 1,334| -12
【ポートフォリオ】
銘柄名|銘柄コード| 枚数| 購入価格| 前日比
------------------------------+----------+----------+----------+----------
日ハム| 2282| 1000| 1,334| -12
実行時間: 0.047 秒.
:
:
(略)
:
:
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
【最終結果】
あなたのロボットによる、該当期間の運用成績は、こちらです。
総資産 = 手持ち資金+所有株式時価総額 = 5,172,000円
単純計算年率利益率 10.0%
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
SystemTradeRsi end
|
まず 2005/03/01 に空売り注文を発行しました。そして 2005/03/25 にドテン買い、2005/04/11 にドテン売り、2005/04/21 にドテン買いを行って、その後 RSI が 75% を超えることはありませんでした。最終結果は 172,000 円の利益となりました。もちろん偶然の要素はあると思いますが、サイコロジカルラインよりも精密な計算をした RSI の方が高い精度でトレードできているようなシミュレーション結果になりました。
偉そうなことを言っていた私の成績は・・・
前回と今回との2回に渡って、カブロボの SDK を実際の投資のシミュレーターとして利用する方法を紹介してきました。カブロボの SDK には過去のデータを参照する機能がはじめから含まれているためこのような使いかたもできるのです。Java のプログラミングは苦手だなあ、という人もこのシミュレーションの機能を使うためだけにチャレンジしてみてもいいのでは?と思うほど便利だと個人的に考えております。
さて、約2ヶ月間に渡って連載を繰り返してきたこの「カブロボ: テクニカル分析と、そのアルゴリズムの紹介」ですが、今回が最終回です。実は他にも色々紹介したいテクニカル分析やリクエストもいただいていたのですが、すべてを紹介することができませんでした。リクエストにお答えできなかった皆様には本当に申し訳ありません。
最後に、この連載を書いていた筆者自身が(それなりに)真剣に作成したカブロボがどの程度のものなのか、現時点(2006年3月28日(火))での運用成績を紹介させていただきます。実は第一回大会では本連載の第三回で紹介した月齢のロボットに少し工夫を加えたもので参加し、結果自作ロボット部門で11位になりました。偶然とは恐ろしいものです(汗)。そして今回は無謀にもベスト10を目標としていました。
そして第二回目の運用状況ですが・・・現在稼動中のロボットは 2006年1月31日から連続稼動しています。そして3月28日現在の資産は・・・ 5,016,000 円でした。利益は年率で 2 %、かろうじて定期預金よりはいいかな、という状態です。
このロボットは移動平均を用いたゴールデンクロスやデッドクロスの例に少し自分なりの工夫を加え、満を持して参加させたロボットでした。実は途中で自作ロボット部門のベスト10に顔を出すこともあったのですが、終わってみれば最後の最後でかろうじてプラスだった、という状態でした。やはり株はよくわかりません(苦笑)。
カブロボの第二回コンテストはこの3月末まで行われております。第3回目以降のコンテストがどのような開催予定になっているのか、どのようなルールで行われるのかなどはわかりませんが、それまでの間はカブロボ SDK を使って投資のシミュレートを行ったり、いざ開催された時のためのロボット開発を今から準備したり、と色々できると思っております。その際にこの連載の内容が参加者の皆様の参考になれば幸いです。
著者について  | |  | 木村 桂: 日本IBM ソフトウェア事業 |
記事の評価
|  |