-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbar.go
138 lines (130 loc) · 3.58 KB
/
bar.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package exch
import (
"bytes"
"encoding/gob"
"time"
)
// Bar 实现了 k 线的相关方法
type Bar struct {
Begin time.Time
Interval time.Duration
Open, High, Low, Close float64 // Price
Volume float64
}
// DecBarFunc 返回的函数会把序列化成 []byte 的 Bar 值转换回来
func DecBarFunc() func(bs []byte) Bar {
var bb bytes.Buffer
dec := gob.NewDecoder(&bb)
return func(bs []byte) Bar {
bb.Reset()
bb.Write(bs)
var bar Bar
dec.Decode(&bar)
return bar
}
}
// newTickBar make the first bar from a tick
func newTickBar(tick Tick, begin time.Time, interval time.Duration) Bar {
tU := tick.Date.Unix()
beginU := begin.Unix()
endU := begin.Add(interval).Unix()
if !(beginU <= tU && tU < endU) {
panic("newTickBar: tick should in begin,interval")
}
return Bar{
Begin: begin,
Interval: interval,
Open: tick.Price,
High: tick.Price,
Low: tick.Price,
Close: tick.Price,
Volume: tick.Volume,
}
}
// TODO: uncomment this block, 添加 bar 生成 bar 的相关方法
// // newBarBar make the first bar from another kind bar
// func newBarBar(bar *Bar, begin time.Time, interval time.Duration) *Bar {
// bInterval := bar.Interval
// if interval <= bInterval {
// panic("newBarBar: 新 bar 应该比旧 bar 宽")
// }
// bBegin, bEnd := bar.Begin, bar.Begin.Add(bInterval)
// end := begin.Add(interval)
// if !(begin.Unix() < bBegin.Unix() && bEnd.Unix() < end.Unix()) {
// panic("newBarBar: 新 bar 应该完全包住旧 bar")
// }
// if interval%bInterval != 0 {
// panic("newBarBar: 新 bar 的宽度,应该是旧 bar 的整数倍")
// }
// if bBegin.Sub(begin)%bInterval != 0 {
// panic("newBarBar: 新旧 bar 要能够对齐")
// }
// res := *bar
// res.Begin = begin
// res.Interval = interval
// return &res
// }
// NilTick 只是一个标志
var NilTick = Tick{}
// GenTickBarFunc 会返回一个接收 tick 并生成 bar 的闭包函数
// 有以下情况需要处理
// 1. 接收第一个 tick,
// 不返回 bar
// 2. 接收到当前的 interval 的 tick
// 不返回 bar
// 3. 接收到下一个 interval 的 tick
// 返回上一个 bar
// 4. 接收到下一个 interval 后面的 interval 的 tick,市场冷清,长时间没有交易
// 返回多个 bar
func GenTickBarFunc(begin BeginFunc, interval time.Duration) func(Tick) []Bar {
isInited := false
var bar Bar
var lastTickDate time.Time
return func(tick Tick) []Bar {
tickBegin := begin(tick.Date, interval)
if !isInited {
bar = newTickBar(tick, tickBegin, interval)
lastTickDate = tick.Date
isInited = true
return nil
}
// 当 tick 发送完毕以后,需要把最后一个 bar 逼出来
if tick == NilTick {
return []Bar{bar}
}
// GenBar 不接受乱序的 ticks
if tick.Date.Before(lastTickDate) {
panic("GenBar: Ticks should be sorted in date")
}
lastTickDate = tick.Date
// 收到了一个本周期的 tick
if tickBegin.Equal(bar.Begin) {
bar.High = maxFloat64(bar.High, tick.Price)
bar.Low = minFloat64(bar.Low, tick.Price)
bar.Close = tick.Price
bar.Volume += tick.Volume
return nil
}
// 收到了若干个周期后的 tick
res := make([]Bar, 0, 256)
for bar.Begin.Before(tickBegin) {
res = append(res, bar)
bar = nextEmptyBar(bar)
}
bar = newTickBar(tick, tickBegin, interval)
return res
}
}
func nextEmptyBar(bar Bar) Bar {
begin := bar.Begin.Add(bar.Interval)
interval := bar.Interval
return Bar{
Begin: begin,
Interval: interval,
Open: bar.Close,
High: bar.Close,
Low: bar.Close,
Close: bar.Close,
Volume: 0,
}
}