レベル: 上級 木村 桂 (kimuc@jp.ibm.com), ソフトウェア事業, IBM
2006年 2月 17日
前回 RCI を用いたテクニカル分析をカブロボに実装する方法を紹介しました。もちろん結果論ではありますが、シミュレート結果がプラスになったことで幸先のよいスタートが切れたものと勝手に解釈しています。
カブロボ 実践編
今回はカブロボ実践編として、数学が好きで好きでたまらない皆様のために、更に高度な標準偏差を用いたテクニカル分析を紹介します。
恐怖の(?)偏差値
「標準偏差」と聞くと 「何それ?」 と反応される方も多いと思います。そこで標準偏差と非常に関係の深い「偏差値」の説明をしておきます。
労働人口年代以下の日本人であれば、おそらくほぼ全員が一度は耳にしたことがある「偏差値」。中には嫌な思い出や印象しかない人がいるかもしれませんが、偏差値自体は非常に合理的な指標であると筆者自身は考えています。
例えば100点満点のある学力試験において、国語と数学でそれぞれ80点を取った場合、これらは同じ価値といえるのでしょうか? また英語が60点だった場合、英語の成績は国語や数学以下だった、と言えるでしょうか? 更に別の例ですが、ともに平均点が50点の歴史と地理において、ともに60点を取った場合、これだけで同じ価値といえるのでしょうか?
どれも言い切れないはずです。例えば国語の平均点が70点で数学は90点だった場合、国語と数学はそもそも平均よりも上か下かのレベルで評価が異なります。また英語の平均点が30点であったとすれば、60点というのは平均の2倍です。もしかするとこの3教科で最も価値のある成績だったといえるかもしれません。
歴史と地理の場合も同様です。平均点という指標を求めた結果が同じであったとしても、一方は0点から100点までまんべんなく分布するような結果になっていて60点は決して珍しくないのに、もう一方は50点の周辺に分布が集中しており、実は60点だけど全体のトップだった、という極端なケースも考えられます。
このように「100点満点中の何点か」、「平均点から何点差があるか」というのは成績を判断する材料の1つではありますが、そもそもその試験自体の難易度によって変わってくるものです。したがって絶対的な点数だけで成績評価はできません。
これに対して標準偏差を用いた「偏差値」という指標で成績を判断した場合は、個別の成績が受験者全体の相対的にどの位置にあるのか、が分かるようになります。ここでは難しい計算の説明は避けますが、
偏差値50は全体のほぼ中心(平均点)。
偏差値60は全体の上位約6分の1(約6人に一人) 偏差値70は全体の上位約40分の1(約40人に一人) :
偏差値40は全体の下位約6分の1(約6人に一人) 偏差値30は全体の下位約40分の1(約40人に一人) :
といった相対的な位置関係がわかるようになります(この位置関係が成立するには、成績が正規分布にしたがっている必要があります。そのためサンプル数が多ければ多いほど、より正確な相対位置を求めることができます)。この偏差値を取り入れることで、試験自体の難易度に左右されない、相対的な成績を求める場合において、この偏差値という指標は非常に便利なものといえます。
なお、標準偏差とは、乱暴な言い方をすると「サンプル数値のばらつき度合い」を示したものです。標準偏差が小さいサンプルでは多くのデータが平均値付近に密集していて、逆に標準偏差が大きいサンプルというのは平均からの乖離の大きなデータも存在する、ということになります。
標準偏差を求める場合、Microsoft Excel であれば STDEV という関数1つで求めることができますが、今回は Java のプログラムで標準偏差を求める必要があります。具体的には以下の計算式で求めることができます:
標準偏差の値 = [ { (サンプルの平均値 - 各サンプル値 ) の二乗 } の和をサンプル数で割ったもの ] の平方根
そして偏差値は以下の計算式で求めたものです:
偏差値 = 50 + 10 * ( サンプルの点数 - 平均点 )/ 標準偏差
株価と偏差値の関係
さて、ではこの偏差値が株やカブロボのアルゴリズムと何の関係があるのでしょうか?
まず偏差値というのは試験結果だけに適用されるものではありません。相当数の数値サンプルがあれば、その平均と標準偏差から偏差値を求めることができます。これは株価という数値でも同じことが言えます。
また上記で説明したように偏差値が70を超えたり、30を下回ることは滅多にありません。この「滅多にない」と判断する臨界点となる数値をいくつにするのか、という判断は難しいのですが、これらのことから
- 直近株価の偏差値が70を超えるような銘柄は(上がりすぎなので)そろそろ下がるのではないか?
- 直近株価の偏差値が30を下回るような銘柄は(下がりすぎなので)そろそろ上がるのではないか?
という仮説を立てて、この条件に合うような銘柄を売買する、という手法が考えられます。 この考え方を取り入れたテクニカル手法としてボリンジャーバンドと呼ばれるテクニカルチャートも存在しています。
カブロボでは日経225内の100銘柄を対象に売買できるようになっています。これだけの銘柄候補があると1日の中で直近株価偏差値が70を超えていたり、30を下回る銘柄は複数存在することが頻繁に出てくると予想されますので、上記ルールをそのまま売買ルールにすることはふさわしくありません。 そこで今回は以下のルールで売買銘柄を決定することにします:
- まず前営業日に注文がなされていた場合は、無条件にその決済注文を行う(つまり注文の翌日に決済する)。
- 各銘柄ごとに株価のサンプルとして最近 20 日間の終値を取得する
- 取得したサンプルから標準偏差を求め、直近終値の偏差値を求める
- この偏差値が70以上か、または30以下の場合は、その銘柄を注文候補とみなす
- その営業日の注文銘柄を以下のルールで決定する
- 注文候補の銘柄が1つも存在しなかった場合、その営業日には新規の注文を行わない
- 注文候補の銘柄が1つだけだった場合は、その銘柄をその営業日の注文銘柄とみなす
- 注文候補の銘柄が複数存在した場合、偏差値が50から最も乖離していた銘柄1つをその日の注文銘柄とみなす
- 注文銘柄が決まった場合、その銘柄を1売買単位の注文を成行で発行する。偏差値が30以下の場合は買い、70以上の場合は売り注文とする
この一連の処理を毎営業日ごとに行うようなロボットを作ることにします。
Java のプログラムで実装
カブロボの環境で実際に動作するロボットを Java で実装した結果が以下になります。なお、開発環境や動作確認の準備等は鳥海様のコラムを参照ください。ここでは run メソッド内のロジックの紹介のみ行います:
適当な名前のロボットを作成し、その run メソッドを以下のように定義します。また平均値と標準偏差を求めるプライベート関数を利用したので、それらも載せておきます。
public void run(InvestmentAgent arg0) {
// TODO Auto-generated method stub
int SIGMADAY = 25; // ここで指定した日数分のサンプルを用いて標準偏差を計算する (0)
Stock[] stocks = arg0.getStocks(); // 銘柄リストの取得
int N = stocks.length; // 銘柄リスト数(今回のルールでは常に 100 のはず)
double s, mins, maxs;
int i, j, minidx, maxidx;
//. ポートフォリオを取得
InformationManager infoManager = arg0.getInformationManager();
Portfolio portfolio = arg0.getPortfolio();
Map holdingMap = portfolio.getHoldings();
// 前営業日に注文した株があれば決済する (1)
if( holdingMap.size() > 0 ){
//. 前営業日に注文した銘柄の情報を取得
Iterator itr = holdingMap.values().iterator();
Holding holding = ( Holding )itr.next();
int tradetype;
// 決済なので、注文した内容が買いであれば売り、売りであれば買いの注文を行う
if( holding.getNumber() > 0 ){
tradetype = StockOrder.SELL;
}else{
tradetype = StockOrder.BUY;
}
// 決済注文
SimpleStockOrder stockOrder = new SimpleStockOrder();
stockOrder.setStock( holding.getStock() ); // 注文の銘柄を設定
stockOrder.setLimitType( StockOrder.MARKET ); // 成り行き注文
stockOrder.setTradeType( tradetype ); // 買い/売り注文を設定
stockOrder.setQuantity( 1 ); // 注文を1単位株に設定
arg0.order( stockOrder ); // 注文の発行
}
// 各銘柄について SIGMADAY 日間の標準偏差を求め、偏差値が最低となっている銘柄を確定させる。
Calendar cdt = Time.getTime(); // 当日の日付
for( i = 0, minidx = -1, mins = 30.0, maxidx = -1, maxs = 70.0; i < N; i ++ ){ // (2)
long cp[] = new long[SIGMADAY];
// 直近の日付から SIGMADAY 日分の銘柄指標データを取得 (3)
List list = infoManager.getIndexInformation( stocks[i], cdt, -1 * SIGMADAY );
for( j = 0; j < SIGMADAY; j ++ ){
// ( SIGMADAY - j )日前のデータ
IndexInformation indexInformation = ( IndexInformation )list.get( j );
// ( SIGMADAY - j )日前の終値
cp[j] = indexInformation.getClosingPrice();
}
// 平均値と標準偏差を求める (4)
double avg = getAVG( cp, SIGMADAY ); //. この銘柄の直近 SIGMADAY 日間の平均株価
double stdev = getSTDEV( cp, SIGMADAY ); //. この銘柄の直近 SIGMADAY 日間の株価標準偏差
// 前営業日の株価の偏差値 (5)
s = 50 + 10 * ( cp[SIGMADAY-1] - avg ) / stdev;
// 必要であればここで標準偏差と偏差値の確認を行う (6)
// System.out.println( stocks[i].getName()
// + "(" + stocks[i].getCode() + ")\t標準偏差: " + stdev + "\t偏差値: " + s );
// 偏差値が最低となっている銘柄を探す (7)
if( s < mins ){
mins = s;
minidx = i;
}
// 偏差値が最高となっている銘柄を探す
if( s > maxs ){
maxs = s;
maxidx = i;
}
}
if( minidx > -1 || maxidx > -1 ){
// (8)
int idx, tradetype;
if( maxs - 50 > 50 - mins ){
idx = maxidx;
s = maxs;
tradetype = StockOrder.SELL;
}else{
idx = minidx;
s = mins;
tradetype = StockOrder.BUY;
}
// (9)
System.out.println( "注文銘柄: " + stocks[idx].getName()
+ "(" + stocks[idx].getCode() + ")\t偏差値: " + s );
// (10)
SimpleStockOrder stockOrder = new SimpleStockOrder();
stockOrder.setStock( stocks[idx] ); // 注文の銘柄を設定
stockOrder.setLimitType( StockOrder.MARKET ); // 成り行き注文
stockOrder.setTradeType( tradetype ); // 買い/売り注文を設定
stockOrder.setQuantity( 1 ); // 注文を1単位株に設定
arg0.order( stockOrder ); // 注文の発行
}
}
//. 平均値
private double getAVG( long[] v, int num ){
double sum = 0.0;
for( int i = 0; i < num; i ++ ){
sum += ( double )v[i];
}
return ( sum / num );
}
//. 標準偏差
private double getSTDEV( long[] v, int num ){
double sum = 0.0;
int n = num - 1;
double avg = getAVG( v, num );
for( int i = 0; i < num; i ++ ){
sum += ( ( double )v[i] - avg ) * ( ( double )v[i] - avg );
}
return Math.sqrt( sum / num );
}
|
以下、簡単にプログラムの内容を解説します。
(0)まず偏差値計算に用いるサンプル数を決定します。多くの場合は1ヶ月分のサンプルを取得するという意味で 20 ~ 25 の数値が用いられます。この例では 25 に設定していますが、ここを変更してもかまいません。
(1)は前回の記事と同様、ポートフォリオを取得した後、その内容を調べて前営業日に成立した注文があるかどうかを確認します。存在していた場合は、その銘柄を決済するため反対売買を行います。
(2)次に各銘柄の偏差値を求めます。ここでは偏差値が 70 を超える中での最大銘柄と、偏差値が 30 を下回る中での最低銘柄の両方を探します。具体的には (0) で指定した日数分の終値を取得し(3)、その平均値と標準偏差を求め(4)、更にそれらの結果をもとにして偏差値を計算します(5)。
ここまでの計算結果をデバッグ用に出力する場合は (6) をコメントアウトしてください。ただこの場合、各営業日ごとに 100 銘柄全ての偏差値を出力することになるため、出力量がかなり多くなります。
そして偏差値が 30 を下回っている銘柄の中での最低と、70 を上回っている中での最大を求めます(7)。
これらが1つでも存在していた場合、再度集計し、偏差値 50 から最も乖離している(つまり最もありえない状況になっている)1つの銘柄を注文銘柄として特定します(8)。特定できたらこれを画面に出力し(9)、実際に1売買単位だけ成行注文します(10)。これがこのプログラムの一連の処理が完成しました。
補足
この標準偏差を使った分析は大まかな考え方として「ありえないレベルになっていたら、そろそろ逆の動きをするであろう」という仮説に基づいています。したがって上にも下にも株価に動きがあるような相場の時にはそれなりの精度で売買できるのですが、上記のプログラムは注文の翌日には決済するようにしており、長期的な視野で利益を確保する、というものではありません。
またこの方法の弱点として、最近の日本株のようにひたすら上昇を続けて、ありえないレベルを更新し続けるような相場になってしまうことを想定していません。
これらの要素があるせいか、筆者がこのロボットをシミュレーションした結果はあまり芳しいものではありませんでした。計算を複雑にすればいいというものではない、ということがよく分かります。
次回予告
次回、内容は未定ですが全く別のテクニカル分析をカブロボで実現する仕組みを紹介する予定です。
著者について  | |  | 木村 桂: 日本IBM ソフトウェア事業 |
記事の評価
|