続・競馬であそぶ 3

 今まさに、小泉破廉恥郎




5. 時系列データ

ちょっと寄り道です。
TARGET の時系列オッズ画面では、オッズ、売上、シェア、人気を区間・累計で見る ことができます。
売上は(計算上の)票数なので異常投票が気になるときはシェアと併せて検討するといいようです。(←受け売り)
sSeia は、低人気の過剰投票?を人気の区間でたまにチェックします。

この表示は非常に見やすいので、自分で時系列用の画面を作る気になりません。
これらのデータを馬券攻略の要素として使うには、マズいろいろ分析しないといけないの ですが、本気
で使いこなすのは結構大変そうです。
経験からのアドバイスを貰ったりしますが、どれも決め手になっているとは思えず、自分 で確認しながら
見極め〜検証をしています。

TARGET とのデータの共存について、ここでは書くつもりはなかったのですが、リアルタ イム系のなかで
特に過去分についてであれば問題が少ないだろうということで、時系列のTARGET 対応 スクリプトを書
いてみました。 競馬であそぶ6 (1) のスクリプトを書き換えただけのものです。
今まで時系列オッズを採っていなかった人にしか役に立たない、用途限定です。

念のため、TARGET の保存用フォルダとは違う場所でテストするのがいいと思います。
★の個所を正しく書き換えて、スクリプト内容を理解した上で使ってください。
年をまたがって取得できません。一年を単位にしています。

#!/usr/bin/perl
# 時系列オッズ取得用のスクリプトです。
use strict ;
use Win32::OLE ;
use Win32::OLE::Variant ;
#===========================================================================#
my $pass = 'h:/DATA/RT_DATA/' ;     # 時系列データ格納場所 ★
my $tpass = 'h:/DATA/SE_DATA/' ;    # TARGET 成績データパス ★
my $year = 2006 ;                   # 年を指定 ★
my $fromm = 1 ;                     # 何月から ★
my $fromd = 28 ;                    # 何日から ★
my $fromt = 0 ;                     # 時〜     ★
# --------------------------------------------------------------------------#
{
    $pass .= 'JI_' . $year ;
    mkdir $pass unless -d $pass ;
    $pass .= '/' ;
}
# --------------------------------------------------------------------------#
my $jktf = [ '0B41',    970,    'JT' ] ;        # 時系列単複枠
my $jkur = [ '0B42',   2050,    'JU' ] ;        # 時系列馬連
############################################################################# 
my $jvd = &jv_first ;       # ← ぜったい必要
&jv_init() ;                # ← ぜったい必要
#---------------------------------------------------------------------------#
&main() ;
&jv_close() ;
################### 以下、JV-Link サブルーチン *****************************#
sub jv_first {
    my $jvd ;
    eval { $jvd = Win32::OLE->GetActiveObject( 'JVDTLab.JVLink' ) } ;
    die "JVLink not installed" if $@ ;
    unless (defined $jvd) {
        $jvd = Win32::OLE->new( 'JVDTLab.JVLink', sub { $_[0]->Quit ; } )
             or die "残念〜。 cannot start JVLink !" ;
    }
    return $jvd ;
}

sub jv_init {       # JVLink 初期化
    my $sid = 'UNKNOWN' ;
    my $rtn = $jvd->JVInit( $sid ) ;
    if( $rtn == 0 ) {
        print "初期化は正常です。\n" ;
        my $version = $jvd->m_JVLinkVersion ;
        print "JVLink Version; $version \n" ;
    } else {
        die "適切な sid ではありません。$rtn\n" ;
    }
}

sub jv_close {          # JVClose
    my $rtn = $jvd->JVClose() ;
    print "JVClose エラー。$rtn\n" if $rtn == -1 ;
}

#===========================================================================#

sub main {
    my @keys ;
    my $ymdbr ;

    my $sfile = "${tpass}${year}/SCHD${year}.dat" ;
    my $ftime = sprintf "%s%02d%02d%02d", $year, $fromm, $fromd, $fromt ;

    open DAT, $sfile or die " Can't open $sfile \n" ;
    while ( <DAT> ) {
        my ( $m, $n ) = unpack '@2 A1 @11 A10', $_ ;
        next unless $m == 3 ;   # 開催された日以外は外す
        next if $n < $ftime ;
        push @keys, $n ;
    }
    close DAT ;
# -----------------------------------------------------------------
    for my $i ( @keys ) {
        for my $j ( 1..12 ) {
            my $jj = sprintf "%02d", $j ;
            $ymdbr = $i . $jj ;
            &get_jikr( $jktf ) ;        # 単複枠
            &get_jikr( $jkur ) ;        # 馬連
        }
    }
    sub get_jikr {  # 引数はリファレンス( 種別ID、仮サイズ、ヘッダ )
        my ( $ds, $bs, $hd ) = @{ shift() } ;
        my $rtn = $jvd->JVRTOpen( $ds, $ymdbr ) ;
        my $i = 0 ;
        RTR: while ( $rtn < -400 or $rtn == -1 ){
            sleep 1 ;
            $rtn = $jvd->JVRTOpen( $ds, $ymdbr ) ;
            $i++ ;
            last RTR if $i > 2 ;
        }
        my $dir = $pass ;
        my ( $nenn, $dname ) = unpack 'A4A4', $ymdbr ;
        $dir .= $dname ;
        mkdir $dir unless -d $dir ;
        $dir .= '/' ;
        if( $rtn == 0 ) {
            my $bsize = Variant( VT_I4, $bs ) ;
            my $pbuf = Variant( VT_UI1,  "\x00" x $bs ) ;
            my $buff = Variant( VT_BSTR|VT_BYREF, \$pbuf ) ;
            my $fn = Variant( VT_BSTR|VT_BYREF, '' ) ;  # ファイル名
            my $flg ;
            my $i = 0 ;
            my $id ;
            my @dat = () ;
            while ( $flg = $jvd->JVRead( $buff, $bsize, $fn ) ) {
                if( $flg > 0 ) {
                    my $line = $buff->Value() ;
                    $id = substr( $line, 11, 16 ) if $i == 0 ;
                    $dat[$i] = $line ;
                    $i++ ;
                } elsif( $flg == -1 ) {
                    my $fname = $dir . $hd . $id . '.DAT' ;
                    open DAT, ">$fname" ;
                    for ( @dat ) {
                        s/\x00+//g ;    s/\r// ;
                        print DAT $_ ;
                    }
                    close DAT ;
                    @dat = () ;     $i = 0 ;
                    print "\t\t読み込み完了。→ : $fn\n" ;
                } else {
                    print " 時系列データ・読み込みエラー。 $flg\n" ;
                }
            }
        } elsif( $rtn == -1 ) {
            print "時系列・該当データがありません。$rtn\n" ;
        } elsif ( $rtn == -504 ) {
            print "サーバーメンテナンス中。\n" ;
        } else {
            print "OPENエラー。$rtn\n" ;
        }
        &jv_close() ;
    }
}
__END__

最近、時系列データを観察するようになってから、これが購入馬券決定に欠かせない要素の一つ だ
ということに気が付きました。

異常投票〜過剰投票という側面にも興味がないわけではないけれど、それが馬券攻略には必ずしも
結びつかないという考えは以前と同じです。ただし、現在の実馬券購入とPAT方式でのGAPを考慮す
ると、簡単に馬を振り分けることができるような気がします。

保存ファイル名は、いままで (無保証の) 取得ファイル名に依存していました。
上記の例では、取得したファイルの中を見てから取得ファイル名を決めるようにしています。
これがファイル名を決める正しい方法の一つの例です。


ついでなので、乗替り初騎乗データからのサブルーチン
と思ったけど、スペースの無駄使いなので削除〜しました。



6. 乗替り初騎乗抽出スクリプト

TARGET を使っている人のための、 Perl スクリプト。
レースID を出力します。

#!/usr/bin/perl -w
# 乗替り初騎乗データをTARGETから抽出するスクリプト
# raceID 場(jo:2)年(yy:2)回(kai:1)日(nch:1)RNo.(rn:2)馬番(ub:2)旧
# 年月日(yyyymmdd:4+2+2)場(jo:2)回(kai:2)日(nch:2)RNo.(rn:2)馬番(ub:2)新
# 2006.02.08
# ★ の部分は、環境に応じて修正

use strict ;
my $dtpass = 'H:/DATA/SE_DATA' ;    # ★ TARGET のSE_DATA パス
my $outF = 'allse.txt' ;            # ★ 出力ファイル名

my $byear = 2000 ;                  # ★ データ取得の最初の年を決める
my $eyear = 2006 ;                  # ★ 最後の年
my $kako = 3 ;                      # ★ 何年前まで( 一応3年 )適当に
my %alls ;

for my $year ( $byear .. $eyear ) {
    my @files = grep { -e } glob "$dtpass/$year/SU*.IDX" ;
    for my $file ( @files ) {
        my $kai = substr ( $file, -6, 1 ) ;     # 回
        my $jo = substr ( $file, -5, 1 ) ;      # 場
        print "$kai $jo \n" ;                   # for DBG
        open DAT, $file or die "Can't Open $file \n" ;
        while ( <DAT> ) {
            chomp ;
            next unless /^\d/ ;                 # 行頭に数字がなければスキップ
            my ( $mmdd, $nch, $rn, $ub, $ucd, $kcd ) =
                unpack '@0 A4 @4 A1 @5 A2 @8 A2 @10 A8 @48 A5', $_ ;
            my $line = join '', $year, $mmdd, $kcd, $jo, $kai, $nch, $rn, $ub ;
            push @{$alls{$ucd}}, $line ;        # ハッシュに配列データを入れる
        }
        close DAT ;
    }
}
# --------------------- ここまでが取得 -- これから出力 -------------------
open OUT, ">$outF" ;
foreach my $cd ( sort keys %alls ) {
    my @allse = @{$alls{$cd}} ;             # 無駄なコピーかもしれないが
    my $num = $#allse ;                     # 最後の添え字( $#allse == @allse - 1 )
    next unless $num > 0 ;                  # 戦歴が1回しかなければパス
    my @sorted = sort { substr( $b, 0, 8 ) <=> substr( $a, 0, 8 ) } @allse ;
    foreach my $i ( 0 .. $num-1 ) {                 # 対象レースを順に
        my ( $year, $mmdd, $kcd, $jo, $knrb ) = unpack 'A4A4A5A1A6', $sorted[$i] ;
        last if $eyear > $year + $kako ;            # 出力期間制限をここでしている
        my $yy = substr( $year, 2, 2) ;             # 西暦年の下2桁
        $jo = $jo eq 'A' ? 10 : sprintf "%02d", $jo ;
        my $raceID = join '', $jo, $yy, $knrb ;     # 繋ぎ合わせて raceID を
        foreach my $j ( $i+1 .. $num ) {            # 過去のレースを順に
            last if $kcd eq substr( $sorted[$j], 8, 5 ) ;
            print OUT "$raceID\n" if $j == $num ;   # 最後まで一致しなければOK!
        }
    }
}
close OUT ;

__END__

TARGET の SE_DATA からの抽出方法は上の通りです。他にも応用が利くと思います。
少し書き換えました。気に入らない部分はドンドン書き直してください。
これを配列だけでやると、データ取得〜表示に殆んど半日かかります。
ハッシュなどを使ったこのスクリプトでは、3年分でも10秒程度です。


念のため、ここに置いておきます ⇒  スクリプト (allse.pl)(2006.02.08)


データルームのデータは、出馬表出力からの集計ですので、この方法とは異なります。
こちらのスクリプトでは、出力対象に障害や(2戦目の)新馬戦も含まれます。
逆に、ES_DATA にある地方・海外のレースを過去走として考慮していません。

まぁここまでやる必要はないと思いますが、追加は簡単?です。
 1. IDXファイルと異なるファイル( & 名 )なので、それにあわせて処理。
 2. 対象レース部分ではスキップ、過去走ではチェック。
 3. 騎手コードが含まれていないので、騎手名から逆引きで取得
どうしても必要な方は挑戦してください。他にもいい方法があるかも。

簡単と書くだけではアレなので、 ここに ⇒  スクリプト (alls.pl)(2006.02.09)

その分、時間は少し余計にかかります。
これで、地方・海外の問題はクリアできていると思います。たぶん。


INDEXへ トップページへ 次へ