2010年7月21日 星期三

可轉債自動報價查詢

關鍵字: perl, python, 可轉債 (convertible bond)


上市公司及上櫃公司的可轉債約有200多卷,可由公開資訊觀測站查詢相關的訊息。以可轉債的特性而言,比較有用的是跌到票面價格以下的債卷,但必須要常常追蹤價格以及市場消息,對於非專業人士實在力有未逮。如果要一筆一筆輸入 Yahoo 的投資組合來 monitor,也是耗時又費力。因此在一台 linux 上面寫了個 perl 的查詢器,再放到 crontab 來定時自動查詢,方便性增加不少。在這邊做個紀錄。

下面是一開始用的 perl script,可以用標準輸入讀入可轉債代號文字檔案 (一個代號一列),查詢後輸出到標準輸出 (csv tab 分隔文字格式)。以下的方法也可以用來查詢一般的股票。


#!/usr/bin/perl -w
use LWP::UserAgent;
use HTTP::Request;
use HTTP::Response;
$ua = new LWP::UserAgent;                               # 產生 UserAgent 物件
my $year     = (localtime)[5] + 1900; # year
my $month   = (localtime)[4] + 1;     # month
my $day      = (localtime)[3];           # day
my $date = $year."-".$month."-".$day;
print "代碼"."\t"."HTTP"."\t"."名稱"."\t"."成交價"."\n";
foreach (<>) {
chomp($_);
$request = new HTTP::Request('GET', 'http://tw.stock.yahoo.com/q/q?s='.$_);     # 產生 Request 物件
$response = $ua->request($request);                     # 開始抓取網頁,並將結果傳會 $response
if ($response->is_success) {                    # 若抓取網頁成功,則印出 HTML 原始碼
        my $html = $response->content;
        if ($html =~ m{href="/q/bc\?s=\d+">(\d+.*?|-)}) {
                print $_."\tOK\t".$1."\t";}
                else {print $_."\tOK\t"."抓不到\t";}
        if ($html =~ m{nowrap>(\d+.?\d+)}) {
                print $1."\n";}
                else {print "抓不到\n";}
        } else {                                                # 若抓取網頁不成功,則印出錯誤訊息
        #print $response->error_as_HTML;
        print $_."\t"."網頁讀取有問題"."\n";
}
}

配合上 crontab 執行以下的 perl script 可以固定每天定時查詢後發信至指定的信箱,包括日期跟時間


#!/usr/bin/perl -w
my $year     = (localtime)[5] + 1900; # year
my $month   = (localtime)[4] + 1;     # month
my $day      = (localtime)[3];           # day
my $hour     = (localtime)[2];           # hour
my $minute  = (localtime)[1];           # minute
my $second = (localtime)[0];           # second
my $cbdate = $year."-".$month."-".$day." ".$hour.":".$minute.":".$second;
my $output = "/tmp/"."cblist-".$year."-".$month."-".$day."-".$hour."-".$minute."-".$second.".txt";


`/path-to-script/queryscript.pl < /convertible_bond_list.txt > $output`;
sleep(25);
open IN, ("< $output");


$to='your@email.address';
$from= 'your@email.address';
$subject='CB Report '.$cbdate;


open(MAIL, "|/usr/sbin/sendmail -t");


## Mail Header
print MAIL "To: $to\n";
print MAIL "From: $from\n";
print MAIL "Subject: $subject\n\n";
## Mail Body
foreach () {
print MAIL $_;
}


close(MAIL);

查完後發現還是不夠完美,我希望可以針對成交價由小至大做排序,這樣只要看前面的幾個可轉債就好了,所以把查詢的  script 用 python 來改寫,輸出 csv 逗號分隔檔至標準輸出裝置,另外增加了 yahoo 上可查詢到的其他欄位。


#! /usr/bin/env python
# -*- coding: big5 -*-
import urllib2, re, sys, csv
print '代碼,HTTP,名稱,成交價,漲跌,買進,賣出,張數,昨收,開盤,最高,最低'
data = []
for line in sys.stdin:
    F1, F2, F3, F4, F5 = '', '', '', '', ''
    F1 = re.match('\d+', line).group(0)
    url = 'http://tw.stock.yahoo.com/q/q?s=' + F1
    myrequest = urllib2.Request(url)
    myrequest.add_header('User-Agent', 'Mozilla 5.0')
    try:
        content = urllib2.urlopen(myrequest)
        F2 = 'OK'
    except:
        F2 = 'Failed'
    c = content.read()
    m = re.search(r'href="/q/bc\?s=\d+">(\d+.*?)', c)
    F3 = m.group(1) if m else '-'
    m = re.search(r'nowrap>(\d+.?\d+|\d+|-)', c)
    F4 = m.group(1) if m else '-'
    m = re.search(r'nowrap>(▽\d+.\d+|△\d+.\d+|\d+.\d+|-)', c)
    F5 = m.group(1) if m else '-'
    F6 = re.findall(r'wrap>(\d+.?\d+|\d+|-)', c)
    data.append([F1, F2, F3, F4, F5]+F6[1:])
for line in sorted(data, key=lambda x: float(x[3]) if (x[3] <> '-') else x[3]):
    result = ''
    for x in line:
        result = result + x + ','
    print result[:-1]

這個  script 有幾個問題
1. 在比大小的時候,python 內建的排序遇到浮點數會被當成字串,因為一開始我們查詢回來的資料就是字串,因此比大小的時候要 type casting。
2. 有用到 key,所以必須要用 python2.4 以上。