mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
add kline dumper
This commit is contained in:
parent
6f16f32e16
commit
234932bc0c
108
pkg/backtest/dumper.go
Normal file
108
pkg/backtest/dumper.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package backtest
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
const DateFormat = "2006-01-02T15:04"
|
||||
|
||||
type symbolInterval struct {
|
||||
Symbol string
|
||||
Interval types.Interval
|
||||
}
|
||||
|
||||
// KLineDumper dumps the received kline data into a folder for the backtest report to load the charts.
|
||||
type KLineDumper struct {
|
||||
OutputDirectory string
|
||||
files map[symbolInterval]*os.File
|
||||
writers map[symbolInterval]*csv.Writer
|
||||
filenames map[symbolInterval]string
|
||||
}
|
||||
|
||||
func NewKLineDumper(outputDirectory string) *KLineDumper {
|
||||
return &KLineDumper{
|
||||
OutputDirectory: outputDirectory,
|
||||
files: make(map[symbolInterval]*os.File),
|
||||
writers: make(map[symbolInterval]*csv.Writer),
|
||||
filenames: make(map[symbolInterval]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *KLineDumper) Filenames() map[symbolInterval]string {
|
||||
return d.filenames
|
||||
}
|
||||
|
||||
func (d *KLineDumper) formatFileName(symbol string, interval types.Interval) string {
|
||||
return filepath.Join(d.OutputDirectory, fmt.Sprintf("%s-%s.csv",
|
||||
symbol,
|
||||
interval))
|
||||
}
|
||||
|
||||
var csvHeader = []string{"date", "startTime", "endTime", "interval", "open", "high", "low", "close", "volume"}
|
||||
|
||||
func (d *KLineDumper) encode(k types.KLine) []string {
|
||||
return []string{
|
||||
time.Time(k.StartTime).Format(time.ANSIC), // ANSIC date - for javascript to parse (this works with Date.parse(date_str)
|
||||
strconv.FormatInt(k.StartTime.Unix(), 10),
|
||||
strconv.FormatInt(k.EndTime.Unix(), 10),
|
||||
k.Interval.String(),
|
||||
k.Open.String(),
|
||||
k.High.String(),
|
||||
k.Low.String(),
|
||||
k.Close.String(),
|
||||
k.Volume.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *KLineDumper) Record(k types.KLine) error {
|
||||
si := symbolInterval{Symbol: k.Symbol, Interval: k.Interval}
|
||||
|
||||
w, ok := d.writers[si]
|
||||
if !ok {
|
||||
filename := d.formatFileName(k.Symbol, k.Interval)
|
||||
f2, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w = csv.NewWriter(f2)
|
||||
d.files[si] = f2
|
||||
d.writers[si] = w
|
||||
d.filenames[si] = filename
|
||||
|
||||
if err := w.Write(csvHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.Write(d.encode(k)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *KLineDumper) Close() error {
|
||||
for _, w := range d.writers {
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
var err error = nil
|
||||
for _, f := range d.files {
|
||||
err2 := f.Close()
|
||||
if err2 != nil {
|
||||
err = multierr.Append(err, err2)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
54
pkg/backtest/dumper_test.go
Normal file
54
pkg/backtest/dumper_test.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package backtest
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
func TestKLineDumper(t *testing.T) {
|
||||
tempDir := os.TempDir()
|
||||
_ = os.Mkdir(tempDir, 0755)
|
||||
dumper := NewKLineDumper(tempDir)
|
||||
|
||||
t1 := time.Now()
|
||||
err := dumper.Record(types.KLine{
|
||||
Exchange: types.ExchangeBinance,
|
||||
Symbol: "BTCUSDT",
|
||||
StartTime: types.Time(t1),
|
||||
EndTime: types.Time(t1.Add(time.Minute)),
|
||||
Interval: types.Interval1m,
|
||||
Open: fixedpoint.NewFromFloat(1000.0),
|
||||
High: fixedpoint.NewFromFloat(2000.0),
|
||||
Low: fixedpoint.NewFromFloat(3000.0),
|
||||
Close: fixedpoint.NewFromFloat(4000.0),
|
||||
Volume: fixedpoint.NewFromFloat(5000.0),
|
||||
QuoteVolume: fixedpoint.NewFromFloat(6000.0),
|
||||
NumberOfTrades: 10,
|
||||
Closed: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = dumper.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
filenames := dumper.Filenames()
|
||||
assert.NotEmpty(t, filenames)
|
||||
for _, filename := range filenames {
|
||||
f, err := os.Open(filename)
|
||||
if assert.NoError(t, err) {
|
||||
reader := csv.NewReader(f)
|
||||
records, err2 := reader.Read()
|
||||
if assert.NoError(t, err2) {
|
||||
assert.NotEmptyf(t, records, "%v", records)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
type BackTestReport struct {
|
||||
type Report struct {
|
||||
Symbol string `json:"symbol,omitempty"`
|
||||
LastPrice fixedpoint.Value `json:"lastPrice,omitempty"`
|
||||
StartPrice fixedpoint.Value `json:"startPrice,omitempty"`
|
||||
|
@ -14,3 +14,4 @@ type BackTestReport struct {
|
|||
InitialBalances types.BalanceMap `json:"initialBalances,omitempty"`
|
||||
FinalBalances types.BalanceMap `json:"finalBalances,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -416,7 +416,7 @@ var BacktestCmd = &cobra.Command{
|
|||
finalBalances.Print()
|
||||
|
||||
if jsonOutputEnabled {
|
||||
result := backtest.BackTestReport{
|
||||
result := backtest.Report{
|
||||
Symbol: symbol,
|
||||
LastPrice: lastPrice,
|
||||
StartPrice: startPrice,
|
||||
|
|
Loading…
Reference in New Issue
Block a user