我正在开发一个机器人交易者(通过 API 与经纪人进行自动交易)。
机器人交易者(windows-)客户端每 10 秒通过 API 访问实时数据(查询数据、买入和卖出)。
为了能够进行测试(在 windows robo-trader 中更改代码并以快速、简单的方式查看结果),我还开发了一个 windows 服务,每 10 秒获取一次数据(如客户端),然后将数据存储在 SQL 服务器上,以便我能够使用 SQL 服务器上的数据(带有历史数据)进行模拟。
基本问题是,数据不同步。
例如。 Windows 客户端获取 10:02:13 的数据,而 Windows 服务获取 10:02:19 的(相同)数据,返回其他数据,并且根据实现的逻辑可能会产生很大的差异。
因此我想将计时器同步到同一秒。
为了达到目标,我实现了两个计时器:
一秒计时器,启动“真正的”10 秒计时器(目标:整分钟)。
十秒计时器,真正发挥作用。
所以...首先启动第二个计时器并等待 50 秒,然后启动十秒计时器。
意味着 10 秒计时器必须在 hh:mm:00(整分钟)开始,然后每 10 秒触发一次。
在 Windows 客户端中,这就像一个魅力:计时器在整分钟启动,并在任意 10 秒(例如 10:02:00、10:02:10、10:02:10 等)触发整个交易日。
在Windows服务中,这个(与客户端中的代码相同)确实 - 无论出于何种原因 - 不起作用(我不明白):
从一秒计时器开始的十秒计时器工作正常(当我在 Windows 事件日志中写入条目时,我可以看到这一点):
十秒计时器在 50 秒时正确启动(例如 10:01:50)。
但是,它会第一次触发,例如在 10:02:03、10:02:13 等 ,然后“宽松”进一步的时间并更改,例如到 11:02:04 等等。
所以..看起来Windows客户端中的计时器与Windows服务中的计时器的工作方式不同,我不明白(这篇文章的原因)。
备注:
所以...问题是,Windows 服务中的 10 秒计时器在 10 秒后不触发(例如,他第一次在 13 秒后触发),在(正确)启动之后“失败”随着他启动的时间越来越长,我绝对不明白(在 Windows 客户端中从未见过这种行为)。
我也尝试过,在 47 秒(而不是 50 秒)时启动计时器,只是为了看看它是否在整分钟开始,情况并非如此,而且让我感到困惑。
代码片段:
定时器定义(在Class()中
Public TimerDatenAbruf As New System.Timers.Timer() ' 10 second timer that does the work
Public TimerSekundenSync As New System.Timers.Timer() ' 1 Second timer that starts the 10 second timer
定时器设置(在OnStart()中
TimerDatenAbruf.Interval = 10 * 1000 ' 10 Seconds for the 10 second timer
TimerSekundenSync.Interval = 1 * 1000 ' 1 Second for the 1 second timer
' Add of the handlers and start of the one second timer
AddHandler TimerSekundenSync.Elapsed, AddressOf SekundenTimerEvent
TimerSekundenSync.Enabled = True
TimerSekundenSync.Start()
AddHandler TimerDatenAbruf.Elapsed, AddressOf TimerDatenAbrufTimerEvent
在 SekundenTimerEvent() 中编写一秒计时器的代码
Private Sub SekundenTimerEvent(source As Object, e As ElapsedEventArgs)
' Sync to the minute
Dim dtTimeStampAktuell As DateTime = System.DateTime.Now
Dim iSekunden As Integer = dtTimeStampAktuell.Second
If iSekunden = 50 Then
If Not TimerDatenAbruf.Enabled Then ' only start, if it don't already runs
TimerDatenAbruf.Start() ' start the ten second timer
TimerSekundenSync.Stop() ' stop the one second timer
End If
End If
End Sub
正如我在上面所写的,十秒计时器是通过上述代码(在 50 秒处)正确启动的,但不要将第一次触发到下一分钟,然后随着运行时间的延长而进一步释放时间。
我希望有人能在黑暗中带来一些光明。
预先感谢您的任何提示。
事实是,您必须像这样调试代码,就好像它将不间断地运行整个处理程序,但知道它可能会在每条指令结束时被中断。
尝试将以下代码作为独立应用程序,看看是否有帮助。它尝试将 DateTime.Now.Second 保持为 10 的偶数倍。以防您无法判断它是 Windows 窗体应用程序。
Public Class Form1
Private Shared ReadOnly InitInterval As Integer = CInt(TimeSpan.FromMilliseconds(5).TotalMilliseconds)
Public WithEvents TenSecTimer As New System.Timers.Timer(InitInterval)
Private Sub TenSecTimer_Elapsed(sender As Object,
e As Timers.ElapsedEventArgs) Handles TenSecTimer.Elapsed
Static stpw As Stopwatch = Stopwatch.StartNew
' Debug.WriteLine(stpw.ElapsedMilliseconds)
Static ProdInterval As Integer = CInt(TimeSpan.FromSeconds(10).TotalMilliseconds)
Static drifted As Boolean = False
Const driftVal As Long = 250 ' 50 for testing, larger for production i.e. 250. not more than 500
Dim dtNOW As DateTime = DateTime.Now
If TenSecTimer.Interval = InitInterval Then
Dim sec As Integer = 60 - dtNOW.Second
If sec <= 1 Then
Threading.Thread.Sleep(57 * 1000) '57 seconds
End If
dtNOW = DateTime.Now
sec = 60 - dtNOW.Second
If sec <> 1 Then
TenSecTimer.Stop()
'when is second 00, hold until it is
sec = 950 * sec
Threading.Thread.Sleep(sec)
sec = 0
Do While DateTime.Now.Second <> 0
sec += 1
Loop
End If
Debug.WriteLine(sec.ToString("n0"))
TenSecTimer.Interval = ProdInterval
TenSecTimer.Start()
ElseIf drifted Then
'reset interval
drifted = False
TenSecTimer.Interval = ProdInterval
ElseIf dtNOW.Millisecond >= driftVal Then
'decrease interval
drifted = True
TenSecTimer.Interval = ProdInterval - driftVal
ElseIf dtNOW.Second Mod 10 <> 0 Then
' hmmmmmm
Debug.Write("* ")
End If
stpw.Restart()
' do real work on a different task!!!
Dim t As Task
t = Task.Run(Sub()
' query the data over the API and store the data on the SQL server
Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff"))
End Sub)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TenSecTimer.Start()
Debug.WriteLine("")
Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff"))
End Sub
End Class