This commit is contained in:
2026-10-07 21:23:21 +08:00
parent 79b02d1c7d
commit 9c8e9d523a
11 changed files with 557 additions and 53 deletions

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
@@ -64,12 +65,21 @@ func main() {
}
fail(err)
}
_, rows, err := orm.GetSeries(exs, *timeframe, *startMS, *endMS, 0, false)
_, rows, err := orm.GetSeries(exs, "1h", *startMS, *endMS, 0, false)
if err != nil {
fail(err)
}
bars := make([]*orm.SeriesOHLCV, 0, len(rows))
for _, row := range rows {
bar, err := row.OHLCV(exs)
if err != nil {
fail(err)
}
bars = append(bars, bar)
}
bars = aggregateHourly(bars, *timeframe)
name := fmt.Sprintf("%d_%s.csv", exs.ID, strings.NewReplacer("/", "_", ":", "_").Replace(pair))
if err := writeCSV(filepath.Join(*outDir, name), rows, exs); err != nil {
if err := writeCSV(filepath.Join(*outDir, name), bars); err != nil {
fail(err)
}
}
@@ -79,7 +89,47 @@ func isUnavailablePair(err *errs.Error) bool {
return err != nil && err.Code == errs.CodeNoMarketForPair
}
func writeCSV(path string, rows []*orm.DataSeries, exs *orm.ExSymbol) error {
// aggregateHourly emits only complete UTC buckets with exactly one bar per hour.
func aggregateHourly(rows []*orm.SeriesOHLCV, timeframe string) []*orm.SeriesOHLCV {
const hourMS int64 = 60 * 60 * 1000
hours := 8
if timeframe == "1d" {
hours = 24
}
stepMS := int64(hours) * hourMS
ordered := append([]*orm.SeriesOHLCV(nil), rows...)
sort.Slice(ordered, func(i, j int) bool { return ordered[i].Time < ordered[j].Time })
var output []*orm.SeriesOHLCV
for first := 0; first < len(ordered); {
bucket := ordered[first].Time / stepMS * stepMS
end := first + 1
for end < len(ordered) && ordered[end].Time/stepMS*stepMS == bucket {
end++
}
complete := end-first == hours
for i := first; complete && i < end; i++ {
complete = ordered[i].Time == bucket+int64(i-first)*hourMS
}
if complete {
bar := *ordered[first]
bar.Time, bar.EndMS, bar.TimeFrame, bar.Closed = bucket, bucket+stepMS, timeframe, true
for _, row := range ordered[first+1 : end] {
bar.High = max(bar.High, row.High)
bar.Low = min(bar.Low, row.Low)
bar.Close = row.Close
bar.Volume += row.Volume
bar.Quote += row.Quote
bar.BuyVolume += row.BuyVolume
bar.TradeNum += row.TradeNum
}
output = append(output, &bar)
}
first = end
}
return output
}
func writeCSV(path string, rows []*orm.SeriesOHLCV) error {
file, err := os.Create(path)
if err != nil {
return err
@@ -89,11 +139,7 @@ func writeCSV(path string, rows []*orm.DataSeries, exs *orm.ExSymbol) error {
if err := writer.Write(columns); err != nil {
return err
}
for _, row := range rows {
bar, err := row.OHLCV(exs)
if err != nil {
return err
}
for _, bar := range rows {
values := []string{
strconv.FormatInt(bar.Time, 10),
strconv.FormatFloat(bar.Open, 'f', -1, 64),