h. 処理のスケジューリング
定時処理
自動取引を行うにあたり、webサイトへのアクセスを定期的に行う必要があります。
例えばループ内で時間をチェックし続けて必要なとき処理を実行するプログラムも考えられますが、ループと条件判定自体は延々と繰り返されてしまいますので、全てのタイミングをこうしたループで待ち受けるのは、定期的な処理を行うのにはあまり良い方法ではないかもしれません。
処理を行う時間が決まっている場合、スケジュールをセットすることで、効率的に処理を行うことができます。
5秒おきにPC時刻を取得してみる
試しに秒が5の倍数になってから5秒おきに30秒間(計6回)、PC時刻を取得して出力するプログラムを書いてみます。
Sub ScheduleTest() 'スケジュールをセットするテスト
Dim i As Integer 'カウンタ
i = 0 'カウンタを一応初期化
Call Schedule(i) 'スケジュールを呼ぶ
End Sub
Sub Schedule(count As Integer) 'スケジュール
While (Second(Time) Mod 5 <> 0) '秒が5の倍数になるタイミングを待つ
DoEvents '制御を渡す
Wend
If count < 6 Then '6回分だけ取得
count = count + 1 '取得回数加算
Application.OnTime Time + TimeSerial(0, 0, 4), _
"'Schedule """ & count & """'" '4秒後にスケジュールをセット
Sheet1.Cells(count, 1) = Time '現在時刻を出力
End If
End Sub
コードを簡単に解説
ScheduleTestからScheduleを呼び出し、その後5秒間隔で合計6回、Application.OnTimeを使用してスケジュールをセットし、自分自身を再帰的に呼び出して実行しています。
Application.OnTimeがややこしいかもしれませんが、第一引数に実行する時間を、第二引数に処理(関数)の名前を指定しています。
更に呼び出す関数(Schedule)に処理の実行回数を知るためのカウンタ(i→count)を引数として渡し、countが6になるまでの間、スケジュールのセットと現在時刻の出力を繰り返します。
5の倍数になるタイミングをループで待ち受けていますが、スケジュールセットが4秒後なので、ループするのはたかだか1秒程度の間です。
5秒間隔なのだから5秒後に予約すればよいと考えるかもしれませんが、そうすると処理にかかる時間の分だけどんどん遅れていきます。
現在時刻を出力する程度の処理に1秒かかることは考えられないので、余裕を持って1秒早く予約し、処理のタイミングを待つようにしています。
時間を扱う場合の注意点
さて、PC時刻を取得してみましたが、実はこの時刻が正確かどうかは分かりません。
というのも、PCの性能によってはPCの内部時刻が本来の時刻から短時間でも秒単位で大きくずれてしまう場合があるからです。
例えば自動取引ツール上で、PC時刻が10:59:55の時に円高を購入しようとしたときに実は10秒遅れていたような場合、その回の購入ができません。
購入できないだけならまだましですが、間違って次回の分を購入してしまうかもしれません。
普段パソコンを使っていてPC時刻の秒単位のずれが気になる人は多くないと思いますが、時間を扱うプログラムを書くときには注意しなければならない問題の一つです。
サーバ時刻に定期的に同期するのも方法の一つですがこれは避けた方がよいと思われます。
時刻を補正する一つの方法
対処法の一つ(これも良い方法かどうか分かりませんが)は、取引対象サイトの時間と同期させることです。
レートを取得するの項でGMOmobileに表示されている時刻を取得しましたが、これを利用します。
以下は「webtime」に正しい時刻が格納されている状態でPC時刻との差「offset」を補正し、補正後の時刻「comptime」に格納するコードの一部です。
PC時刻自体を補正するのではなくPC時刻を「時間のずれた時計」として考えています。上記のように一致させたい時刻とのずれが分かるので、目的を考えるとこれで十分だと思います。
補正した時刻情報が必要な場合、comptimeを更新してそれを参照するようにします。