« 「マーケットプロファイル分析」を読んだ | トップページ | Perlを使ってオプション価格を求める »

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;
  }

|

« 「マーケットプロファイル分析」を読んだ | トップページ | Perlを使ってオプション価格を求める »

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

コメント

makoさん こんにちは。
私もperlで投資をしています。

win32::ddeを使う時にこちらの情報を参考にさせていただきました。
ホットリンクに関しては気になってましたので、
win32::dde_hotlinkモジュールを書いてみました。

http://www.link.gr.jp

投稿: bottle | 2009年4月16日 (木) 22時21分

bottleさん、コメントありがとうございます。
この話題についてネット上に情報が少ないこともあり試行錯誤の経過を残していましたが、お役にたてたようでなによりです。

投稿: mako | 2009年4月21日 (火) 21時00分

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: Perlを使ってDDE通信でRSSにアクセスする:

« 「マーケットプロファイル分析」を読んだ | トップページ | Perlを使ってオプション価格を求める »