Skip to content

Commit 008d86e

Browse files
committed
Update
1 parent 200b9e4 commit 008d86e

File tree

4 files changed

+207
-59
lines changed

4 files changed

+207
-59
lines changed

backtest/backtest.go

Lines changed: 142 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
slog "log"
1616
"os"
1717
"path/filepath"
18-
"sort"
1918
"time"
2019
)
2120

@@ -58,8 +57,9 @@ type PlotData struct {
5857
}
5958

6059
type DataState struct {
61-
Time int64 // ns
62-
Index int // datas 中的索引
60+
PrevTime int64 // ns
61+
Time int64 // ns
62+
Index int // datas 中的索引
6363
}
6464

6565
type Backtest struct {
@@ -72,7 +72,7 @@ type Backtest struct {
7272
start time.Time // 开始时间
7373
end time.Time // 结束时间
7474
currentTimeNS int64 // ns
75-
sortedDatas []*DataState
75+
timeNsDatas []int64
7676

7777
startedAt time.Time // 运行开始时间
7878
endedAt time.Time // 运行结束时间
@@ -179,18 +179,11 @@ func (b *Backtest) Run() {
179179
data.Reset(b.start, b.end)
180180
}
181181

182-
// 初始化数据
183-
b.sortedDatas = make([]*DataState, 0, len(b.datas))
184-
for i := 0; i < len(b.datas); i++ {
185-
b.sortedDatas = append(b.sortedDatas, &DataState{
186-
Time: 0,
187-
Index: i,
188-
})
182+
if !b.next() {
183+
log.Error("error")
184+
return
189185
}
190186

191-
// 设置数据缓存
192-
b.setDataCache()
193-
194187
// 初始净值
195188
strategyTester.addInitItemStats()
196189
strategyTester.OnInit()
@@ -215,60 +208,163 @@ func (b *Backtest) Run() {
215208
b.endedAt = time.Now()
216209
}
217210

211+
// 新的 next 方法
218212
func (b *Backtest) next() bool {
219-
if b.currentTimeNS == 0 {
220-
return b.nextInternal()
213+
if len(b.datas) == 1 {
214+
return b.nextOne()
221215
}
222216

223-
for _, data := range b.sortedDatas {
224-
if b.currentTimeNS < data.Time {
225-
b.currentTimeNS = data.Time
226-
return true
217+
if b.currentTimeNS == 0 {
218+
for _, data := range b.datas {
219+
if !data.Next() {
220+
return false
221+
}
222+
}
223+
224+
b.resetSortedDatas()
225+
226+
// 取时间最大项
227+
b.currentTimeNS = b.timeNsDatas[len(b.timeNsDatas)-1]
228+
n := len(b.datas)
229+
for i := 0; i < n; i++ {
230+
data := b.datas[i]
231+
for {
232+
if data.GetOrderBook().Time.UnixNano() >= b.currentTimeNS {
233+
break
234+
}
235+
if !data.Next() {
236+
return false
237+
}
238+
}
227239
}
240+
return true
228241
}
229242

230-
return b.nextInternal()
231-
}
243+
for {
244+
for _, timeNs := range b.timeNsDatas {
245+
if b.currentTimeNS < timeNs {
246+
b.currentTimeNS = timeNs
247+
if !b.ensureMoveNext(b.currentTimeNS) {
248+
return false
249+
}
250+
return true
251+
}
252+
}
232253

233-
func (b *Backtest) nextInternal() bool {
234-
if len(b.datas) == 1 {
235-
ret := b.datas[0].Next()
236-
if ret {
237-
b.currentTimeNS = b.datas[0].GetOrderBook().Time.UnixNano()
254+
for _, data := range b.datas {
255+
if !data.Next() {
256+
return false
257+
}
238258
}
239-
return ret
259+
260+
b.resetSortedDatas()
240261
}
262+
}
241263

242-
for _, data := range b.datas {
243-
if !data.Next() {
244-
return false
264+
func (b *Backtest) ensureMoveNext(ns int64) bool {
265+
n := len(b.datas)
266+
count := 0
267+
for i := 0; i < n; i++ {
268+
data := b.datas[i]
269+
for {
270+
if data.GetOrderBook().Time.UnixNano() >= ns {
271+
break
272+
}
273+
if !data.Next() {
274+
return false
275+
}
276+
count++
245277
}
246278
}
247-
248-
b.setDataCache()
249-
279+
if count > 0 {
280+
// 重新排序
281+
b.resetSortedDatas()
282+
}
250283
return true
251284
}
252285

253-
func (b *Backtest) setDataCache() {
254-
// 数据对齐,提前排序
255-
n := len(b.datas)
256-
if n == 0 {
257-
return
286+
func (b *Backtest) resetSortedDatas() {
287+
nDatas := len(b.datas)
288+
if len(b.timeNsDatas) != nDatas*2 {
289+
b.timeNsDatas = make([]int64, nDatas*2)
258290
}
259291

260-
for i := 0; i < n; i++ {
261-
b.sortedDatas[i].Time = b.datas[i].GetOrderBook().Time.UnixNano()
262-
b.sortedDatas[i].Index = i
292+
for i := 0; i < nDatas; i++ {
293+
index := i * 2
294+
b.timeNsDatas[index] = b.datas[i].GetOrderBookRaw(1).Time.UnixNano()
295+
b.timeNsDatas[index+1] = b.datas[i].GetOrderBook().Time.UnixNano()
263296
}
264297

265-
sort.Slice(b.sortedDatas, func(i, j int) bool {
266-
return b.sortedDatas[i].Time < b.sortedDatas[j].Time
267-
})
298+
utils.SortInt64(b.timeNsDatas)
299+
}
268300

269-
b.currentTimeNS = b.sortedDatas[0].Time
301+
func (b *Backtest) nextOne() bool {
302+
ret := b.datas[0].Next()
303+
if ret {
304+
b.currentTimeNS = b.datas[0].GetOrderBook().Time.UnixNano()
305+
}
306+
return ret
270307
}
271308

309+
//func (b *Backtest) next() bool {
310+
// if b.currentTimeNS == 0 {
311+
// return b.nextInternal()
312+
// }
313+
//
314+
// for _, data := range b.sortedDatas {
315+
// if b.currentTimeNS < data.Time {
316+
// b.currentTimeNS = data.Time
317+
// return true
318+
// }
319+
// }
320+
//
321+
// return b.nextInternal()
322+
//}
323+
324+
//func (b *Backtest) nextInternal() bool {
325+
// if len(b.datas) == 1 {
326+
// ret := b.datas[0].Next()
327+
// if ret {
328+
// b.currentTimeNS = b.datas[0].GetOrderBook().Time.UnixNano()
329+
// }
330+
// return ret
331+
// }
332+
//
333+
// for _, data := range b.datas {
334+
// if !data.Next() {
335+
// return false
336+
// }
337+
// }
338+
//
339+
// b.setDataCache()
340+
//
341+
// return true
342+
//}
343+
344+
//func (b *Backtest) setDataCache() {
345+
// // 数据对齐,提前排序
346+
// n := len(b.datas)
347+
// if n == 0 {
348+
// return
349+
// }
350+
//
351+
// for i := 0; i < n; i++ {
352+
// b.sortedDatas[i].Time = b.datas[i].GetOrderBook().Time.UnixNano()
353+
// b.sortedDatas[i].Index = i
354+
// }
355+
//
356+
// sort.Slice(b.sortedDatas, func(i, j int) bool {
357+
// return b.sortedDatas[i].Time < b.sortedDatas[j].Time
358+
// })
359+
//
360+
// if b.currentTimeNS == 0 {
361+
// // 第一次按右对齐
362+
// b.currentTimeNS = b.sortedDatas[len(b.sortedDatas)-1].Time
363+
// } else {
364+
// b.currentTimeNS = b.sortedDatas[0].Time
365+
// }
366+
//}
367+
272368
func (b *Backtest) GetPrices() (result []float64) {
273369
n := len(b.datas)
274370
result = make([]float64, n)

dataloader/data.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,38 @@ func (d *Data) Reset(start time.Time, end time.Time) {
3333
d.maxIndex = len(d.data) - 1
3434
}
3535

36+
func (d *Data) GetOrderBookByNS(ns int64) *OrderBook {
37+
if d.data == nil {
38+
return nil
39+
}
40+
ob := d.data[d.index]
41+
if ob.Time.UnixNano() <= ns {
42+
return ob
43+
} else if ob = d.GetOrderBookRaw(1); ob != nil && ob.Time.UnixNano() <= ns {
44+
return ob
45+
} else {
46+
return nil
47+
}
48+
}
49+
3650
func (d *Data) GetOrderBook() *OrderBook {
3751
if d.data == nil {
3852
return nil
3953
}
4054
return d.data[d.index]
4155
}
4256

57+
func (d *Data) GetOrderBookRaw(offset int) *OrderBook {
58+
if d.data == nil {
59+
return nil
60+
}
61+
index := d.index - offset
62+
if index < 0 {
63+
return nil
64+
}
65+
return d.data[index]
66+
}
67+
4368
func (d *Data) GetRecords(size int) []*Record {
4469
return nil
4570
}
@@ -49,25 +74,36 @@ func (d *Data) Next() bool {
4974
d.index++
5075
return true
5176
}
52-
if n := d.readMore(); n > 0 {
53-
d.index = 0
77+
if o, n := d.readMore(); n > 0 {
78+
d.index = o
5479
d.maxIndex = n - 1
5580
return true
5681
}
5782
return false
5883
}
5984

60-
func (d *Data) readMore() int {
85+
func (d *Data) readMore() (offset int, count int) {
6186
if !d.dataLoader.HasMoreData() {
62-
return 0
87+
return 0, 0
6388
}
6489
data := d.dataLoader.ReadOrderBooks()
6590
if len(data) == 0 {
66-
return 0
91+
return 0, 0
92+
}
93+
d.offset += len(data)
94+
dataCount := len(d.data)
95+
if dataCount > 0 {
96+
n := 5 // 需要保留的周期
97+
if dataCount < n {
98+
n = dataCount
99+
}
100+
d.data = append(d.data[dataCount-n:], data...)
101+
offset = n
102+
} else {
103+
d.data = data
67104
}
68-
d.offset += len(d.data)
69-
d.data = data
70-
return len(data)
105+
count = len(d.data)
106+
return
71107
}
72108

73109
func NewData(loader DataLoader) *Data {

exchanges/spotsim/spotsim.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,14 @@ func (s *SpotSim) GetBalance(currency string) (result *SpotBalance, err error) {
5656

5757
// 获取订单薄(OrderBook)
5858
func (s *SpotSim) GetOrderBook(symbol string, depth int) (result *OrderBook, err error) {
59-
result = s.data.GetOrderBook()
59+
result = s.data.GetOrderBookByNS(s.backtest.GetTime().UnixNano())
6060
return
6161
}
6262

63+
func (s *SpotSim) getOrderBook() *OrderBook {
64+
return s.data.GetOrderBookByNS(s.backtest.GetTime().UnixNano())
65+
}
66+
6367
// 获取K线数据
6468
// period: 数据周期. 分钟或者关键字1m(minute) 1h 1d 1w 1M(month) 1y 枚举值:1 3 5 15 30 60 120 240 360 720 "5m" "4h" "1d" ...
6569
func (s *SpotSim) GetRecords(symbol string, period string, from int64, end int64, limit int) (records []*Record, err error) {
@@ -85,7 +89,6 @@ func (s *SpotSim) PlaceOrder(symbol string, direction Direction, orderType Order
8589
}
8690
params := ParsePlaceOrderParameter(opts...)
8791
id := GenOrderId()
88-
//ob := s.data.GetOrderBook()
8992
order := &Order{
9093
ID: id,
9194
Symbol: symbol,
@@ -147,7 +150,7 @@ func (s *SpotSim) matchMarketOrder(order *Order) (match bool, err error) {
147150
return
148151
}
149152

150-
ob := s.data.GetOrderBook()
153+
ob := s.getOrderBook()
151154

152155
// 市价成交
153156
if order.Direction == Buy {
@@ -205,7 +208,7 @@ func (s *SpotSim) matchLimitOrder(order *Order, immediate bool) (match bool, err
205208
return
206209
}
207210

208-
ob := s.data.GetOrderBook()
211+
ob := s.getOrderBook()
209212
if order.Direction == Buy { // Bid order
210213
if order.Price >= ob.AskPrice() {
211214
if immediate && order.PostOnly {
@@ -412,7 +415,7 @@ func (s *SpotSim) RunEventLoopOnce() (err error) {
412415
}
413416

414417
func (s *SpotSim) logOrderInfo(msg string, event string, order *Order) {
415-
ob := s.data.GetOrderBook()
418+
ob := s.getOrderBook()
416419
baseBalance := s.balance.Base.Available + s.balance.Base.Frozen
417420
quoteBalance := s.balance.Quote.Available + s.balance.Quote.Frozen
418421
s.eLog.Infow(

utils/sort.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package utils
2+
3+
import "sort"
4+
5+
// SortInt64 sorts a slice of int64s in increasing order.
6+
func SortInt64(a []int64) { sort.Sort(SortInt64Slice(a)) }
7+
8+
// SortInt64Slice attaches the methods of Interface to []int64, sorting in increasing order.
9+
type SortInt64Slice []int64
10+
11+
func (p SortInt64Slice) Len() int { return len(p) }
12+
func (p SortInt64Slice) Less(i, j int) bool { return p[i] < p[j] }
13+
func (p SortInt64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

0 commit comments

Comments
 (0)