Skip to content

Commit 8cd9e76

Browse files
committed
fix buildAggSeries for custom series
1 parent 313f63a commit 8cd9e76

5 files changed

Lines changed: 136 additions & 22 deletions

File tree

data/common.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ func (j *PairTFCache) fillLacks(pair string, subTfSecs int, startMS, endMS int64
262262
j.WaitBar = nil
263263
if len(preRows) > 0 {
264264
fromTFMS := int64(subTfSecs * 1000)
265-
oldBars, _, err := buildOHLCVSeries(exs, j.TimeFrame, preRows, tfMSecs, 0, nil, fromTFMS, j.AlignOffMS, false)
265+
oldBars, _, err := buildAggSeries(exs, j.TimeFrame, preRows, tfMSecs, 0, nil, fromTFMS, j.AlignOffMS, false)
266266
if err != nil {
267267
return nil, err
268268
}

data/feeder.go

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -311,25 +311,16 @@ func applyAdjSeriesList(exs *orm.ExSymbol, adjs []*orm.AdjInfo, rows []*orm.Data
311311
return orm.BarsToSeries(exs, rows[0].TimeFrame, orm.ApplyAdj(adjs, bars, adjMode, cutEnd, limit), nil, rows[0].IsWarmUp, true)
312312
}
313313

314-
func buildOHLCVSeries(exs *orm.ExSymbol, tf string, rows []*orm.DataSeries, toTFMSecs int64, preFire float64, prev []*orm.DataSeries, fromTFMS, offMS int64, opts ...bool) ([]*orm.DataSeries, bool, *errs.Error) {
314+
func buildAggSeries(exs *orm.ExSymbol, tf string, rows []*orm.DataSeries, toTFMSecs int64, preFire float64, prev []*orm.DataSeries, fromTFMS, offMS int64, opts ...bool) ([]*orm.DataSeries, bool, *errs.Error) {
315315
isWarmUp := false
316316
if len(opts) > 0 {
317317
isWarmUp = opts[len(opts)-1]
318318
}
319-
bars, err := orm.SeriesToBars(exs, rows)
320-
if err != nil {
321-
return nil, false, err
322-
}
323-
prevBars, err := orm.SeriesToBars(exs, prev)
319+
aggRows, lastOk, err := orm.ResampleDataSeries(exs, tf, rows, prev, toTFMSecs, preFire, fromTFMS, offMS, isWarmUp)
324320
if err != nil {
325-
return nil, false, err
326-
}
327-
aggBars, lastOk := utils.BuildOHLCV(bars, toTFMSecs, preFire, prevBars, fromTFMS, offMS)
328-
targetTF := tf
329-
if targetTF == "" {
330-
targetTF = utils2.SecsToTF(int(toTFMSecs / 1000))
321+
return nil, false, errs.New(core.ErrInvalidBars, err)
331322
}
332-
return orm.BarsToSeries(exs, targetTF, aggBars, nil, isWarmUp, true), lastOk, nil
323+
return aggRows, lastOk, nil
333324
}
334325

335326
type IDataFeeder interface {
@@ -551,7 +542,7 @@ func (f *SeriesFeeder) onNewData(barTfMSecs int64, rows []*orm.DataSeries) (bool
551542
if state.WaitBar != nil {
552543
olds = append(olds, state.WaitBar)
553544
}
554-
ohlcvs, lastOk, err = buildOHLCVSeries(f.ExSymbol, state.TimeFrame, rows, staMSecs, f.PreFire, olds, barTfMSecs, state.AlignOffMS, false, f.isWarmUp)
545+
ohlcvs, lastOk, err = buildAggSeries(f.ExSymbol, state.TimeFrame, rows, staMSecs, f.PreFire, olds, barTfMSecs, state.AlignOffMS, false, f.isWarmUp)
555546
if err != nil {
556547
return false, err
557548
}
@@ -584,7 +575,7 @@ func (f *SeriesFeeder) onNewData(barTfMSecs int64, rows []*orm.DataSeries) (bool
584575
if barTfMSecs < staMSecs {
585576
// The last unfinished data should be kept here
586577
// 这里应该保留最后未完成的数据
587-
ohlcvs, _, err = buildOHLCVSeries(f.ExSymbol, state.TimeFrame, rows, staMSecs, f.PreFire, nil, barTfMSecs, state.AlignOffMS, false, f.isWarmUp)
578+
ohlcvs, _, err = buildAggSeries(f.ExSymbol, state.TimeFrame, rows, staMSecs, f.PreFire, nil, barTfMSecs, state.AlignOffMS, false, f.isWarmUp)
588579
if err != nil {
589580
return false, err
590581
}
@@ -612,7 +603,7 @@ func (f *SeriesFeeder) onNewData(barTfMSecs int64, rows []*orm.DataSeries) (bool
612603
olds = append(olds, state.WaitBar)
613604
}
614605
bigTfMSecs := int64(state.TFSecs * 1000)
615-
curOhlcvs, lastDone, err := buildOHLCVSeries(f.ExSymbol, state.TimeFrame, curRows, bigTfMSecs, f.PreFire, olds, srcMSecs, srcAlignOff, false, f.isWarmUp)
606+
curOhlcvs, lastDone, err := buildAggSeries(f.ExSymbol, state.TimeFrame, curRows, bigTfMSecs, f.PreFire, olds, srcMSecs, srcAlignOff, false, f.isWarmUp)
616607
if err != nil {
617608
return false, err
618609
}

data/watcher.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ func (w *SeriesWatcher) onSpiderBar(raw *utils.IOMsgRaw) {
273273
olds = append(olds, job.WaitBar)
274274
}
275275
jobMSecs := int64(job.TFSecs * 1000)
276-
aggRows, lastDone, err := buildOHLCVSeries(orm.GetExSymbol2(exgName, market, pair), utils2.SecsToTF(job.TFSecs), curRows, jobMSecs, 0, olds, tfMSecs, job.AlignOffMS, false)
276+
aggRows, lastDone, err := buildAggSeries(orm.GetExSymbol2(exgName, market, pair), utils2.SecsToTF(job.TFSecs), curRows, jobMSecs, 0, olds, tfMSecs, job.AlignOffMS, false)
277277
if err != nil {
278278
log.Error("build series fail", zap.String("pair", pair), zap.Error(err))
279279
return

orm/series.go

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package orm
22

33
import (
44
"fmt"
5+
"sort"
56
"strconv"
67

78
"github.com/banbox/banexg"
@@ -342,10 +343,7 @@ func ResampleSeriesRecords(info *SeriesInfo, exs *ExSymbol, rows []*DataRecord,
342343
}
343344
values := make(map[string]any, len(fields))
344345
for _, field := range fields {
345-
fn, ok := GetAggRuleFunc("last")
346-
if exs != nil {
347-
fn, ok = GetAggRuleFunc(exs.AggRule(field.Name))
348-
}
346+
fn, ok := GetAggRuleFunc(seriesAggRule(info, exs, field.Name))
349347
if !ok {
350348
fn, _ = GetAggRuleFunc("last")
351349
}
@@ -386,6 +384,109 @@ func ResampleSeriesRecords(info *SeriesInfo, exs *ExSymbol, rows []*DataRecord,
386384
return out, nil
387385
}
388386

387+
func ResampleDataSeries(exs *ExSymbol, tf string, rows, prev []*DataSeries, toTFMS int64, preFire float64, fromTFMS, offMS int64, isWarmUp bool) ([]*DataSeries, bool, error) {
388+
if len(rows) == 0 {
389+
return nil, false, nil
390+
}
391+
if tf == "" {
392+
tf = utils2.SecsToTF(int(toTFMS / 1000))
393+
}
394+
source := NormalizeSeriesSource(rows[0].Source)
395+
info := NewSeriesInfo(source, tf, inferSeriesFields(prev, rows))
396+
records := make([]*DataRecord, 0, len(prev)+len(rows))
397+
for _, row := range prev {
398+
if rec := SeriesToRecord(row); rec != nil {
399+
records = append(records, rec)
400+
}
401+
}
402+
for _, row := range rows {
403+
if rec := SeriesToRecord(row); rec != nil {
404+
records = append(records, rec)
405+
}
406+
}
407+
offsetMS := int64(float64(toTFMS)*preFire) + offMS
408+
aggRecords, err := ResampleSeriesRecords(info, exs, records, toTFMS, offsetMS)
409+
if err != nil {
410+
return nil, false, err
411+
}
412+
out := RecordsToSeries(info, exs, aggRecords)
413+
for _, row := range out {
414+
row.IsWarmUp = isWarmUp
415+
}
416+
lastFinished := false
417+
if fromTFMS > 0 && len(out) > 0 {
418+
_, offset := utils2.GetTfAlignOrigin(int(toTFMS / 1000))
419+
alignOffMS := int64(offset * 1000)
420+
finishMS := utils2.AlignTfMSecsOffset(rows[len(rows)-1].TimeMS+fromTFMS+offsetMS, toTFMS, alignOffMS)
421+
lastFinished = finishMS > out[len(out)-1].TimeMS
422+
}
423+
return out, lastFinished, nil
424+
}
425+
426+
func inferSeriesFields(groups ...[]*DataSeries) []SeriesField {
427+
seen := make(map[string]any)
428+
for _, rows := range groups {
429+
for _, row := range rows {
430+
if row == nil {
431+
continue
432+
}
433+
for key, val := range row.Values {
434+
if _, ok := seen[key]; !ok {
435+
seen[key] = val
436+
}
437+
}
438+
}
439+
}
440+
keys := make([]string, 0, len(seen))
441+
for key := range seen {
442+
keys = append(keys, key)
443+
}
444+
sort.Strings(keys)
445+
fields := make([]SeriesField, 0, len(keys))
446+
for _, key := range keys {
447+
fields = append(fields, SeriesField{Name: key, Type: inferSeriesFieldType(seen[key])})
448+
}
449+
return fields
450+
}
451+
452+
func inferSeriesFieldType(val any) string {
453+
switch val.(type) {
454+
case bool:
455+
return "bool"
456+
case string:
457+
return "string"
458+
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
459+
return "int"
460+
case float32, float64:
461+
return "float"
462+
default:
463+
return "json"
464+
}
465+
}
466+
467+
func seriesAggRule(info *SeriesInfo, exs *ExSymbol, field string) string {
468+
if exs != nil {
469+
if rule, ok := exs.AggRuleMap()[field]; ok {
470+
return rule
471+
}
472+
}
473+
if info != nil && NormalizeSeriesSource(info.Name) == SeriesSourceKline {
474+
switch field {
475+
case "open":
476+
return "first"
477+
case "high":
478+
return "max"
479+
case "low":
480+
return "min"
481+
case "close":
482+
return "last"
483+
case "volume", "quote", "buy_volume", "trade_num":
484+
return "sum"
485+
}
486+
}
487+
return "last"
488+
}
489+
389490
func seriesFloatValue(values map[string]any, key string) (float64, error) {
390491
if values == nil {
391492
return 0, fmt.Errorf("series event missing field %q", key)

orm/series_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,28 @@ func TestResampleSeriesRecordsUsesAggRules(t *testing.T) {
111111
}
112112
}
113113

114+
func TestResampleDataSeriesUsesAggRulesForGenericSeries(t *testing.T) {
115+
exs := &ExSymbol{ID: 3, AggRules: `{"rate":"avg","volume":"sum"}`}
116+
rows := []*DataSeries{
117+
{Source: "funding", Sid: 3, TimeMS: 1_700_000_040_000, EndMS: 1_700_000_100_000, Closed: true, Values: map[string]any{"rate": 1.0, "volume": 2.0}},
118+
{Source: "funding", Sid: 3, TimeMS: 1_700_000_100_000, EndMS: 1_700_000_160_000, Closed: true, Values: map[string]any{"rate": 3.0, "volume": 5.0}},
119+
}
120+
121+
got, done, err := ResampleDataSeries(exs, "2m", rows, nil, 120_000, 0, 60_000, 0, true)
122+
if err != nil {
123+
t.Fatalf("ResampleDataSeries returned error: %v", err)
124+
}
125+
if !done || len(got) != 1 {
126+
t.Fatalf("expected one finished row, done=%v len=%d", done, len(got))
127+
}
128+
if got[0].Source != "funding" || got[0].TimeFrame != "2m" || !got[0].IsWarmUp {
129+
t.Fatalf("unexpected series header: %+v", got[0])
130+
}
131+
if got[0].Values["rate"] != 2.0 || got[0].Values["volume"] != 7.0 {
132+
t.Fatalf("unexpected values: %+v", got[0].Values)
133+
}
134+
}
135+
114136
func testPairTFKline(symbol, tf string, ts int64) *banexg.PairTFKline {
115137
return &banexg.PairTFKline{
116138
Kline: banexg.Kline{Time: ts},

0 commit comments

Comments
 (0)