Files
cbpred/cmd/cbpred-export/main.go
2026-09-17 22:02:51 +08:00

120 lines
3.2 KiB
Go

package main
import (
"encoding/csv"
"flag"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/banbox/banbot/biz"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/orm"
"github.com/banbox/banexg/errs"
)
var columns = []string{
"timestamp", "open", "high", "low", "close", "volume", "quote", "buy_volume", "trade_num",
}
func main() {
configPath := flag.String("config", "", "banbot config path")
dataDir := flag.String("data-dir", "", "banbot data directory")
pairs := flag.String("pairs", "", "comma-separated symbols")
startMS := flag.Int64("start-ms", 0, "inclusive start timestamp in milliseconds")
endMS := flag.Int64("end-ms", 0, "exclusive end timestamp in milliseconds")
timeframe := flag.String("timeframe", "8h", "bar timeframe (8h or 1d)")
outDir := flag.String("out-dir", "", "CSV output directory")
skipUnavailable := flag.Bool("skip-unavailable", true, "skip pairs unavailable on the exchange and absent from the local cache")
flag.Parse()
if (*timeframe != "8h" && *timeframe != "1d") || *configPath == "" || *dataDir == "" || *pairs == "" || *outDir == "" || *startMS <= 0 || *endMS <= *startMS {
flag.Usage()
os.Exit(2)
}
args := &config.CmdArgs{
Configs: config.ArrString{*configPath},
DataDir: *dataDir,
NoDefault: true,
RawPairs: *pairs,
TimeStart: strconv.FormatInt(*startMS, 10),
TimeEnd: strconv.FormatInt(*endMS, 10),
LogLevel: "error",
}
if err := biz.SetupComs(args); err != nil {
fail(err)
}
defer core.RunExitCalls()
if err := os.MkdirAll(*outDir, 0o755); err != nil {
fail(err)
}
for _, rawPair := range strings.Split(*pairs, ",") {
pair := strings.TrimSpace(rawPair)
if pair == "" {
continue
}
exs, err := orm.GetExSymbolCur(pair)
if err != nil {
if *skipUnavailable && isUnavailablePair(err) {
continue
}
fail(err)
}
_, rows, err := orm.GetSeries(exs, *timeframe, *startMS, *endMS, 0, false)
if err != nil {
fail(err)
}
name := fmt.Sprintf("%d_%s.csv", exs.ID, strings.NewReplacer("/", "_", ":", "_").Replace(pair))
if err := writeCSV(filepath.Join(*outDir, name), rows, exs); err != nil {
fail(err)
}
}
}
func isUnavailablePair(err *errs.Error) bool {
return err != nil && err.Code == errs.CodeNoMarketForPair
}
func writeCSV(path string, rows []*orm.DataSeries, exs *orm.ExSymbol) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
writer := csv.NewWriter(file)
if err := writer.Write(columns); err != nil {
return err
}
for _, row := range rows {
bar, err := row.OHLCV(exs)
if err != nil {
return err
}
values := []string{
strconv.FormatInt(bar.Time, 10),
strconv.FormatFloat(bar.Open, 'f', -1, 64),
strconv.FormatFloat(bar.High, 'f', -1, 64),
strconv.FormatFloat(bar.Low, 'f', -1, 64),
strconv.FormatFloat(bar.Close, 'f', -1, 64),
strconv.FormatFloat(bar.Volume, 'f', -1, 64),
strconv.FormatFloat(bar.Quote, 'f', -1, 64),
strconv.FormatFloat(bar.BuyVolume, 'f', -1, 64),
strconv.FormatInt(bar.TradeNum, 10),
}
if err := writer.Write(values); err != nil {
return err
}
}
writer.Flush()
return writer.Error()
}
func fail(err error) {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}