カテゴリー「システムトレード」の記事

2007年6月30日 (土)

Perlを使ってオプション価格を求める

日経平均オプション取引をするにあたり、オプションの理論価格を求めたりIVを求めたりするのにツールがあったほうがよいらしい。Excelのものはどこかにあるのだろうけど、せっかくPerlを使えるんだし、Perlでそういうモジュールがないかどうか探してみた。

CPANで見つかったのは下の2つ。

どちらもブラックショールズモデルを使っている。例によってインターネット上であまり情報が見つからないこともあって自分でこれらの2つを試してみたが、結論からいうとどっちもどっちであり、組み合わせて使うなどの工夫が必要とわかった。

Finance-Options-Calcは、Activeperlでppmを使ってinstallでき、原資産価格、行使価格、残存日数、Volatility、金利の上方を入力して、理論価格やデルタ/セータ/ロー/ガンマ/ベガを計算することが可能。オプション価格からIVを計算することはできない。

一方、Math-Business-BlackScholesは、ppmでinstallできず、Windowsで使うならcygwinのperlとして使う必要がある。Math-CDFというモジュールが必要であり、これもppmなしである。オプション価格からIVを計算したり、過去の値動きからHVを計算することもできる。ただし、デルタ/セータ/ロー/ガンマ/ベガを計算する機能はない。

マーケットスピードのシミュレータを使い、同じ値を入力して同じ値が帰ってくるかどうかテストしてみた。マーケットスピードは残存日数として休日の考慮がなく、365日計算となっており、自作プログラムも同様にした。IVなどを入力値としてオプション理論価格を求めたところ、Math-Business-BlackScholesはマケスピと同じ値となったが、Finance-Options-Calcは10~20円程度違う値となってしまい、どっちが正しいかわからない状況となった。モジュールのソースを見比べて調べたところ、累積正規分布を求める処理に違いがあることがわかり、Finance-Options-Calcで_normという関数で求めているものを、Math-Business-BlackScholesで使っているMath-CDFのpnormと置き換えたところ、Finance-Options-Calcもマケスピと同じ値が算出できた。デルタ/セータ/ロー/ガンマ/ベガも値が一致した。

以上より、IVやHVを求めるときにはMath-Business-BlackScholesを使用し、デルタ/セータ/ロー/ガンマ/ベガを求めるときには、pnormで改造したFinance-Options-Calcを使えばいいことになる。

他に気づいた相違点として、Math-Business-BlackScholesにはブラックショールズの拡張として対象証券からのキャッシュ・フローを考慮することができる( $fractional_yield として指定。省略すると0となる)。そういえば、入力データの指定方法としてパーセントにしたものを使うか、パーセントにしないで小数点表記のもの(100倍しない)ものを使うかという違いもある。後は、残存日数として日を指定するか年を指定するかという違いもある。Finance-Options-Calcは日を指定するが、実際は内部処理で365で割っているだけだが。

しかし、デルタ・ニュートラル・ヘッジ戦法は難しいと感じる。もう少し簡単な戦略で損失限定のポジを作って実践することから試してみるか。水曜日あたりはIVが高くなっていたので売り時だったなぁ。

| | コメント (0) | トラックバック (0)

2007年5月27日 (日)

Perlを使ってDDE通信でRSSにアクセスする

楽天証券がDLJ時代から提供しているRSS (Realtime SpreadSheet; Blogなどを読むRSSとは違う)を使って、Excelでリアルタイム株価を取得することができる。RSS自体はDDEというWindowsのプロセス間通信のしくみを使っており、クライアントプログラムを作成すればExcel以外からも使うことができる。

ということで、自分が使い慣れているPerlでDDE通信するプログラムを作成することにした。

PerlでDDE通信をするのに最も簡単な方法は、Win32::DDEというモジュールを使うことだ。ActivePerl標準ではないので、ここを参考にしてppmの設定をしてインストールする。

使いかたは以下のような感じになる。

use Win32::DDE::Client;

my $Server = 'RSS'; # サービス名「RSS」
my $Topic = 'N225.FUT01.OS'; # トピック名 この例は日経平均先物
my $Price = '現在値'; #アイテム名 この例は現在値

#First, Connect DDE
my $Client = new Win32::DDE::Client ($Server, $Topic);
die "Unable to initiate conversation" if $Client->Error;

defined ($Rcvprice = $Client->Request ($Price)) ||
  print "DDE request failed\n";
# 結果は小数点2桁となっているので小数点以下を消す (17580.00を17580にする)
$Rcvprice =~ s/^  (\d+).(\d+)$/\1/;

$Client->Disconnect;

意外と簡単にできる。ただ、Win32::DDEを使う方法にはいくつか問題点がある。

一番大きな問題は、hotlinkが動作しないということである。ExcelのRSSは、株価が変わったらそのことをサーバがクライアントに通知(XTYP_ADVDATA)し、クライアントはそれを受けてからサーバに株価データを取りに行くというホットリンククライアントとなっているが、Perlを使うとそれがうまく動作しない。これは、海外のサイト含めていろいろと調べたり、Win32::DDEを使わずにWin32::APIでDDE関連のwin32api関数をコールするようにしたり、待機するしくみとしてsleep関数ではなくPOEをつかったり、とかなりの試行錯誤をしたんだけど、どうしてもできなかった。Win32::DDEには、テストされていないと書いてあるけどXTYP_ADVSTARTを送る関数があったりコールバック関数を登録することができたりするが、DDEサーバであるRSSがXTYP_ADVDATAを送っているにもかかわらず、それをPerl側で拾えないという感じだった。(RSSがXTYP_ADVDATAを送っていることは、DDE通信を見るツールで確認した。DDE通信を見る方法としてDDESpyというものがあるらしいが、これは何かの有料の開発キットに入っているものらしく利用できなかった。代わりに、こちらのサイトにあるDynamic Data Studioというものを使った。) ただ、クライアント側からデータ取得などでDDE通信をしているときに発生したXTYP_ADVDATAは拾うことができ、実際にコールバック関数が呼ばれた。たまにできることがあるが、基本的にはNGというやっかいな現象だった。

次に問題となるのは、上の例でいう$Client->Request でのデータ取得を16384回以上やるとエラーとなってしまうことである。この原因は、上記の試行錯誤でWin32::APIのwin32api関数をコールするコードを書いているときに判明したが、Win32::DDE::ClientのRequestの処理中に取得したDDEオブジェクトハンドルを解放しないままにしていることが根本原因と分かった。日経平均先物の歩み値を取得するプログラムを書いていたが、1秒に1回データを取得するようにすると、売買が活発な後場にぎりぎり足りなくなるということがあって困っていた。解決策としては、そんなにデータを取得しないようにするか、もしくは根本対策としてWin32::DDE::ClientのRequestで取得したDDEオブジェクトハンドルを解放するようにするか、もしくはWin32::APIを使ってDDE通信をするようにし、その中でDDEオブジェクトハンドルを解放するようにすることである。これにより、0.2秒ごとにデータを取得しても問題なく、10万回データ取得しても問題なくなった。

もう一つ問題があって、たまに以下のようなエラーが出てしまってプログラムが終了してしまうことがある。
Undefined subroutine &main:: called at c:/Perl/site/lib/Win32/DDE/Callback.pm line 46.
これは、newでコールバック関数を登録していても発生していた。該当箇所を見たが、なぜかコールバックが呼ばれたときにうまくコールバック関数自体を読み込めていないというように理解できた。この解決策は、Callback.pmの該当箇所 (&$runsub のところ) をコメントにして無効化してしまうことである。ちなみに、試しにコメントになっている&main::TestCBという部分を有効にし、TestCBという関数をプログラムで用意したところ、その関数が呼ばれ、XTYP_REGISTERを受信していることが分かった。上記の試行錯誤をやっているときに気づいたが、Perlのプログラムで3分などの周期的にデータ取得するプログラムが動作している状況で、場中に適時開示情報などでIEで別の窓を作ってPDF文書を開いた後にデータ取得をしたタイミングでXTYP_REGISTERを受信していることが分かった。XTYP_REGISTER を受け取らないようにするには、DdeInitializeのafCmdでCBF_SKIP_REGISTRATIONSを指定すればよいが、afCmdはWin32::DDEでは決めうちで0になっている。

Win32::APIを使ってDDE通信を記述するには、win32apiのDDE関連を理解する必要がある。本家のMSDNや、こちらのサイトの記述(Window SDK編 第2部第150章第155章)が参考になった。また、DDE関連のヘッダファイル(ddeml.h)はここにあった。これがないと定数の値などが分からなくて困る。

ホットリンクを使うにはPerlをあきらめてC#でも勉強するしかないと覚悟したが、なんとかごまかしが効きそうだったので、しばらくはPerlでやっていく予定。C#を勉強するいい機会とは思ったんだけど。

[2007/06/30追記]

Win32::DDE::Clientで16384回以上のRequestをするとエラーとなる件について、Client.pmに対して根本対策のコードを追加した。たった1行だけど参考に書いておく。

*** Client.pm.orig Sun Oct 12 05:59:44 1997
--- Client.pm Sat Jun 30 23:26:22 2007
***************
*** 102,107 ****
--- 102,108 ----
  Win32::DDE::DdeGetData($hdata, $buf, $size, 0);
  $buf =~ s/\0.*$//s;
  $ret = $buf;
+  Win32::DDE::DdeFreeDataHandle($hdata);
  } else {
  $ret = undef;
  }

| | コメント (2) | トラックバック (0)