Skip to content

Commit 91aba2e

Browse files
committed
wip reviewable
1 parent cb68de4 commit 91aba2e

File tree

4 files changed

+137
-76
lines changed

4 files changed

+137
-76
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package csvsource
2+
3+
import (
4+
"encoding/csv"
5+
"time"
6+
7+
"github.com/c9s/bbgo/pkg/fixedpoint"
8+
"github.com/c9s/bbgo/pkg/types"
9+
)
10+
11+
type ICSVTickConverter interface {
12+
LatestKLine() (k *types.KLine)
13+
GetKLineResult() []types.KLine
14+
CsvTickToKLine(tick *CsvTick, interval types.Interval)
15+
}
16+
17+
// CSVTickConverter takes a tick and internally converts it to a KLine slice
18+
type CSVTickConverter struct {
19+
csv *csv.Reader
20+
decoder CSVTickDecoder
21+
klines []types.KLine
22+
}
23+
24+
func NewCSVTickConverter() ICSVTickConverter {
25+
return &CSVTickConverter{
26+
klines: []types.KLine{},
27+
}
28+
}
29+
30+
// GetKLineResult returns the converted ticks as kLine of interval
31+
func (c *CSVTickConverter) LatestKLine() (k *types.KLine) {
32+
if len(c.klines) == 0 {
33+
return nil
34+
}
35+
return &c.klines[len(c.klines)-1]
36+
}
37+
38+
// GetKLineResult returns the converted ticks as kLine of interval
39+
func (c *CSVTickConverter) GetKLineResult() []types.KLine {
40+
return c.klines
41+
}
42+
43+
// Convert ticks to KLine with interval
44+
func (c *CSVTickConverter) CsvTickToKLine(tick *CsvTick, interval types.Interval) {
45+
var (
46+
currentCandle = types.KLine{}
47+
high = fixedpoint.Zero
48+
low = fixedpoint.Zero
49+
)
50+
isOpen, t := c.detCandleStart(tick.Timestamp.Time(), interval)
51+
52+
if isOpen {
53+
c.klines = append(c.klines, types.KLine{
54+
StartTime: types.NewTimeFromUnix(t.Unix(), 0),
55+
EndTime: types.NewTimeFromUnix(t.Add(interval.Duration()).Unix(), 0),
56+
Open: tick.Price,
57+
High: tick.Price,
58+
Low: tick.Price,
59+
Close: tick.Price,
60+
Volume: tick.HomeNotional,
61+
})
62+
return
63+
}
64+
65+
currentCandle = c.klines[len(c.klines)-1]
66+
67+
if tick.Price.Float64() > currentCandle.High.Float64() {
68+
high = tick.Price
69+
} else {
70+
high = currentCandle.High
71+
}
72+
73+
if tick.Price.Float64() < currentCandle.Low.Float64() {
74+
low = tick.Price
75+
} else {
76+
low = currentCandle.Low
77+
}
78+
79+
c.klines[len(c.klines)-1] = types.KLine{
80+
StartTime: currentCandle.StartTime,
81+
EndTime: currentCandle.EndTime,
82+
Open: currentCandle.Open,
83+
High: high,
84+
Low: low,
85+
Close: tick.Price,
86+
Volume: currentCandle.Volume.Add(tick.HomeNotional),
87+
}
88+
}
89+
90+
func (c *CSVTickConverter) detCandleStart(ts time.Time, interval types.Interval) (isOpen bool, t time.Time) {
91+
if len(c.klines) == 0 {
92+
return true, interval.Convert(ts)
93+
}
94+
var (
95+
current = c.klines[len(c.klines)-1]
96+
end = current.EndTime.Time()
97+
)
98+
if ts.After(end) {
99+
return true, end
100+
}
101+
102+
return false, t
103+
}

pkg/datasource/csvsource/csv_tick_reader.go

Lines changed: 6 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,17 @@ package csvsource
33
import (
44
"encoding/csv"
55
"io"
6-
"time"
76

8-
"github.com/c9s/bbgo/pkg/fixedpoint"
97
"github.com/c9s/bbgo/pkg/types"
108
)
119

1210
var _ TickReader = (*CSVTickReader)(nil)
1311

1412
// CSVTickReader is a CSVTickReader that reads from a CSV file.
1513
type CSVTickReader struct {
16-
csv *csv.Reader
17-
decoder CSVTickDecoder
18-
klines []types.KLine
14+
csv *csv.Reader
15+
decoder CSVTickDecoder
16+
converter *CSVTickConverter
1917
}
2018

2119
// MakeCSVTickReader is a factory method type that creates a new CSVTickReader.
@@ -26,7 +24,6 @@ func NewCSVTickReader(csv *csv.Reader) *CSVTickReader {
2624
return &CSVTickReader{
2725
csv: csv,
2826
decoder: BinanceCSVTickDecoder,
29-
klines: []types.KLine{},
3027
}
3128
}
3229

@@ -35,12 +32,12 @@ func NewCSVTickReaderWithDecoder(csv *csv.Reader, decoder CSVTickDecoder) *CSVTi
3532
return &CSVTickReader{
3633
csv: csv,
3734
decoder: decoder,
38-
klines: []types.KLine{},
3935
}
4036
}
4137

4238
// ReadAll reads all the KLines from the underlying CSV data.
4339
func (r *CSVTickReader) ReadAll(interval types.Interval) (k []types.KLine, err error) {
40+
converter := NewCSVTickConverter()
4441
var i int
4542
for {
4643
tick, err := r.Read(i)
@@ -54,10 +51,10 @@ func (r *CSVTickReader) ReadAll(interval types.Interval) (k []types.KLine, err e
5451
if tick == nil {
5552
continue
5653
}
57-
r.CsvTickToKLines(tick, interval)
54+
converter.CsvTickToKLine(tick, interval)
5855
}
5956

60-
return k, nil
57+
return converter.GetKLineResult(), nil
6158
}
6259

6360
// Read reads the next KLine from the underlying CSV data.
@@ -69,65 +66,3 @@ func (r *CSVTickReader) Read(i int) (*CsvTick, error) {
6966

7067
return r.decoder(rec, i)
7168
}
72-
73-
// Convert ticks to KLine with interval
74-
func (c *CSVTickReader) CsvTickToKLines(tick *CsvTick, interval types.Interval) {
75-
var (
76-
currentCandle = types.KLine{}
77-
high = fixedpoint.Zero
78-
low = fixedpoint.Zero
79-
)
80-
isOpen, t := c.detCandleStart(tick.Timestamp.Time(), interval)
81-
82-
if isOpen {
83-
c.klines = append(c.klines, types.KLine{
84-
StartTime: types.NewTimeFromUnix(t.Unix(), 0),
85-
EndTime: types.NewTimeFromUnix(t.Add(interval.Duration()).Unix(), 0),
86-
Open: tick.Price,
87-
High: tick.Price,
88-
Low: tick.Price,
89-
Close: tick.Price,
90-
Volume: tick.HomeNotional,
91-
})
92-
return
93-
}
94-
95-
currentCandle = c.klines[len(c.klines)-1]
96-
97-
if tick.Price.Float64() > currentCandle.High.Float64() {
98-
high = tick.Price
99-
} else {
100-
high = currentCandle.High
101-
}
102-
103-
if tick.Price.Float64() < currentCandle.Low.Float64() {
104-
low = tick.Price
105-
} else {
106-
low = currentCandle.Low
107-
}
108-
109-
c.klines[len(c.klines)-1] = types.KLine{
110-
StartTime: currentCandle.StartTime,
111-
EndTime: currentCandle.EndTime,
112-
Open: currentCandle.Open,
113-
High: high,
114-
Low: low,
115-
Close: tick.Price,
116-
Volume: currentCandle.Volume.Add(tick.HomeNotional),
117-
}
118-
}
119-
120-
func (c *CSVTickReader) detCandleStart(ts time.Time, interval types.Interval) (isOpen bool, t time.Time) {
121-
if len(c.klines) == 0 {
122-
return true, interval.Convert(ts)
123-
}
124-
var (
125-
current = c.klines[len(c.klines)-1]
126-
end = current.EndTime.Time()
127-
)
128-
if ts.After(end) {
129-
return true, end
130-
}
131-
132-
return false, t
133-
}

pkg/datasource/csvsource/stream.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func (s *Stream) simulateEvents() error {
6363
}
6464
//nolint:errcheck // Read ops only so safe to ignore err return
6565
defer file.Close()
66+
converter := NewCSVTickConverter()
6667
reader := NewBybitCSVTickReader(csv.NewReader(file))
6768
tick, err := reader.Read(i)
6869
if err != nil {
@@ -72,14 +73,14 @@ func (s *Stream) simulateEvents() error {
7273
if err != nil {
7374
return err
7475
}
75-
s.StandardStream.EmitMarketTrade(trade)
76+
s.StandardStream.EmitMarketTrade(*trade)
7677

77-
reader.CsvTickToKLines(tick, s.config.Interval)
78-
kline := klines[len(klines)-1]
78+
converter.CsvTickToKLine(tick, s.config.Interval)
79+
kline := converter.LatestKLine()
7980
if kline.Closed {
80-
s.StandardStream.EmitKLineClosed(kline)
81+
s.StandardStream.EmitKLineClosed(*kline)
8182
} else {
82-
s.StandardStream.EmitKLine(kline)
83+
s.StandardStream.EmitKLine(*kline)
8384
}
8485

8586
return nil

pkg/datasource/csvsource/types.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,25 @@ type CsvTick struct {
1515
ForeignNotional fixedpoint.Value `json:"foreignNotional"`
1616
Timestamp types.MillisecondTimestamp `json:"timestamp"`
1717
}
18+
19+
// todo
20+
func (c *CsvTick) toGlobalTrade() (*types.Trade, error) {
21+
return &types.Trade{
22+
// ID: tradeIdNum,
23+
// OrderID: orderIdNum,
24+
// Exchange: types.ExchangeBybit,
25+
// Price: trade.OrderPrice,
26+
// Quantity: trade.OrderQty,
27+
// QuoteQuantity: trade.OrderPrice.Mul(trade.OrderQty),
28+
// Symbol: trade.Symbol,
29+
// Side: side,
30+
// IsBuyer: side == types.SideTypeBuy,
31+
// IsMaker: isMaker,
32+
// Time: types.Time(trade.ExecutionTime),
33+
// Fee: trade.ExecFee,
34+
// FeeCurrency: trade.FeeTokenId,
35+
// IsMargin: false,
36+
// IsFutures: false,
37+
// IsIsolated: false,
38+
}, nil
39+
}

0 commit comments

Comments
 (0)