From 461735e04390f6fbc9e9af4f38b061ca017607ac Mon Sep 17 00:00:00 2001 From: c9s Date: Sat, 22 Jul 2023 11:36:04 +0800 Subject: [PATCH 1/5] grid2: add remove duplicated pins and pull out filter price prec func --- pkg/strategy/grid2/grid.go | 45 +++++++++++++++++++++-------- pkg/strategy/grid2/grid_test.go | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 12 deletions(-) diff --git a/pkg/strategy/grid2/grid.go b/pkg/strategy/grid2/grid.go index 84e646733..06587b997 100644 --- a/pkg/strategy/grid2/grid.go +++ b/pkg/strategy/grid2/grid.go @@ -35,28 +35,49 @@ type Grid struct { type Pin fixedpoint.Value +// filterPrice filters price with the given precision +func filterPrice(p fixedpoint.Value, prec int) fixedpoint.Value { + var pow10 = math.Pow10(prec) + pp := math.Round(p.Float64()*pow10*10.0) / 10.0 + pp = math.Trunc(pp) / pow10 + + pps := strconv.FormatFloat(pp, 'f', prec, 64) + price := fixedpoint.MustNewFromString(pps) + return price +} + +func removeDuplicatedPins(pins []Pin) []Pin { + var buckets = map[string]struct{}{} + var out []Pin + + for _, pin := range pins { + p := fixedpoint.Value(pin) + + if _, exists := buckets[p.String()]; exists { + continue + } else { + out = append(out, pin) + } + + buckets[p.String()] = struct{}{} + } + + return out +} + func calculateArithmeticPins(lower, upper, spread, tickSize fixedpoint.Value) []Pin { var pins []Pin // tickSize number is like 0.01, 0.1, 0.001 var ts = tickSize.Float64() var prec = int(math.Round(math.Log10(ts) * -1.0)) - var pow10 = math.Pow10(prec) for p := lower; p.Compare(upper.Sub(spread)) <= 0; p = p.Add(spread) { - pp := math.Round(p.Float64()*pow10*10.0) / 10.0 - pp = math.Trunc(pp) / pow10 - - pps := strconv.FormatFloat(pp, 'f', prec, 64) - price := fixedpoint.MustNewFromString(pps) + price := filterPrice(p, prec) pins = append(pins, Pin(price)) } // this makes sure there is no error at the upper price - pp := math.Round(upper.Float64()*pow10*10.0) / 10.0 - pp = math.Trunc(pp) / pow10 - - pps := strconv.FormatFloat(pp, 'f', prec, 64) - upperPrice := fixedpoint.MustNewFromString(pps) + upperPrice := filterPrice(upper, prec) pins = append(pins, Pin(upperPrice)) return pins @@ -94,7 +115,7 @@ func (g *Grid) CalculateGeometricPins() { return nil } - g.addPins(g.calculator()) + g.addPins(removeDuplicatedPins(g.calculator())) } func (g *Grid) CalculateArithmeticPins() { diff --git a/pkg/strategy/grid2/grid_test.go b/pkg/strategy/grid2/grid_test.go index 8176ad8d0..ac47f3072 100644 --- a/pkg/strategy/grid2/grid_test.go +++ b/pkg/strategy/grid2/grid_test.go @@ -214,3 +214,54 @@ func Test_calculateArithmeticPins(t *testing.T) { }) } } + +func Test_filterPrice1(t *testing.T) { + type args struct { + p fixedpoint.Value + prec int + } + tests := []struct { + name string + args args + want string + }{ + { + name: "basic", + args: args{p: number("31.2222"), prec: 3}, + want: "31.222", + }, + { + name: "roundup", + args: args{p: number("31.22295"), prec: 3}, + want: "31.223", + }, + { + name: "roundup2", + args: args{p: number("31.22290"), prec: 3}, + want: "31.222", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rst := filterPrice(tt.args.p, tt.args.prec) + assert.Equalf(t, tt.want, rst.String(), "filterPrice(%v, %v)", tt.args.p, tt.args.prec) + }) + } +} + +func Test_removeDuplicatedPins(t *testing.T) { + pins := []Pin{ + Pin(number("31.222")), + Pin(number("31.222")), + Pin(number("31.223")), + Pin(number("31.224")), + Pin(number("31.224")), + } + out := removeDuplicatedPins(pins) + assert.Equal(t, []Pin{ + Pin(number("31.222")), + Pin(number("31.223")), + Pin(number("31.224")), + }, out) + +} From df1067d309eb6ca553fe5f56457313a78dfed7b1 Mon Sep 17 00:00:00 2001 From: c9s Date: Sat, 22 Jul 2023 11:45:30 +0800 Subject: [PATCH 2/5] grid2: simplify removeDuplicatedPins --- pkg/strategy/grid2/grid.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/strategy/grid2/grid.go b/pkg/strategy/grid2/grid.go index 06587b997..8156382af 100644 --- a/pkg/strategy/grid2/grid.go +++ b/pkg/strategy/grid2/grid.go @@ -55,10 +55,9 @@ func removeDuplicatedPins(pins []Pin) []Pin { if _, exists := buckets[p.String()]; exists { continue - } else { - out = append(out, pin) } + out = append(out, pin) buckets[p.String()] = struct{}{} } From aac288426cf524edf19fcccd890e0cdc8ba97190 Mon Sep 17 00:00:00 2001 From: c9s Date: Sat, 22 Jul 2023 11:47:03 +0800 Subject: [PATCH 3/5] update github workflow branch patterns --- .github/workflows/go.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7413c6e12..a0e21c855 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -4,7 +4,9 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] + branches: + - "main" + - "v*" jobs: build: From 5f2ead4ffd8e68243df266d71c855b2837e6adbc Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 24 Jul 2023 14:57:50 +0800 Subject: [PATCH 4/5] maxapi: parse fd field and optimize trade snapshot parsing --- pkg/exchange/max/maxapi/userdata.go | 50 +++++++++--------------- pkg/exchange/max/maxapi/userdata_test.go | 45 +++++++++++++++++++++ 2 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 pkg/exchange/max/maxapi/userdata_test.go diff --git a/pkg/exchange/max/maxapi/userdata.go b/pkg/exchange/max/maxapi/userdata.go index 8fe3e3c5d..000e65946 100644 --- a/pkg/exchange/max/maxapi/userdata.go +++ b/pkg/exchange/max/maxapi/userdata.go @@ -100,10 +100,12 @@ type TradeUpdate struct { Volume string `json:"v"` Market string `json:"M"` - Fee string `json:"f"` - FeeCurrency string `json:"fc"` - Timestamp int64 `json:"T"` - UpdateTime int64 `json:"TU"` + Fee string `json:"f"` + FeeCurrency string `json:"fc"` + FeeDiscounted bool `json:"fd"` + + Timestamp int64 `json:"T"` + UpdateTime int64 `json:"TU"` OrderID uint64 `json:"oi"` @@ -128,40 +130,26 @@ func parseTradeUpdate(v *fastjson.Value) TradeUpdate { type TradeUpdateEvent struct { BaseEvent - Trades []TradeUpdate `json:"t"` } -func parseTradeUpdateEvent(v *fastjson.Value) *TradeUpdateEvent { - var e TradeUpdateEvent - e.Event = string(v.GetStringBytes("e")) - e.Timestamp = v.GetInt64("T") - - for _, tv := range v.GetArray("t") { - e.Trades = append(e.Trades, parseTradeUpdate(tv)) - } - - return &e -} - -type TradeSnapshot []TradeUpdate - type TradeSnapshotEvent struct { BaseEvent - Trades []TradeUpdate `json:"t"` } -func parseTradeSnapshotEvent(v *fastjson.Value) *TradeSnapshotEvent { +func parseTradeUpdateEvent(v *fastjson.Value) (*TradeUpdateEvent, error) { + jsonBytes := v.String() + var e TradeUpdateEvent + err := json.Unmarshal([]byte(jsonBytes), &e) + return &e, err +} + +func parseTradeSnapshotEvent(v *fastjson.Value) (*TradeSnapshotEvent, error) { + jsonBytes := v.String() var e TradeSnapshotEvent - e.Event = string(v.GetStringBytes("e")) - e.Timestamp = v.GetInt64("T") - - for _, tv := range v.GetArray("t") { - e.Trades = append(e.Trades, parseTradeUpdate(tv)) - } - - return &e + err := json.Unmarshal([]byte(jsonBytes), &e) + return &e, err } type BalanceMessage struct { @@ -252,10 +240,10 @@ func ParseUserEvent(v *fastjson.Value) (interface{}, error) { return parseOrderUpdateEvent(v), nil case "trade_snapshot", "mwallet_trade_snapshot": - return parseTradeSnapshotEvent(v), nil + return parseTradeSnapshotEvent(v) case "trade_update", "mwallet_trade_update": - return parseTradeUpdateEvent(v), nil + return parseTradeUpdateEvent(v) case "ad_ratio_snapshot", "ad_ratio_update": return parseADRatioEvent(v) diff --git a/pkg/exchange/max/maxapi/userdata_test.go b/pkg/exchange/max/maxapi/userdata_test.go new file mode 100644 index 000000000..af80dcd22 --- /dev/null +++ b/pkg/exchange/max/maxapi/userdata_test.go @@ -0,0 +1,45 @@ +package max + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/valyala/fastjson" +) + +func Test_parseTradeSnapshotEvent(t *testing.T) { + fv, err := fastjson.Parse(`{ + "c": "user", + "e": "trade_snapshot", + "t": [{ + "i": 68444, + "p": "21499.0", + "v": "0.2658", + "M": "ethtwd", + "T": 1521726960357, + "sd": "bid", + "f": "3.2", + "fc": "twd", + "fd": false, + "m": true, + "oi": 7423, + "ci": "client-oid-1", + "gi": 123 + }], + "T": 1591786735192 + }`) + assert.NoError(t, err) + assert.NotNil(t, fv) + + evt, err := parseTradeSnapshotEvent(fv) + assert.NoError(t, err) + assert.NotNil(t, evt) + assert.Equal(t, "trade_snapshot", evt.Event) + assert.Equal(t, int64(1591786735192), evt.Timestamp) + assert.Equal(t, 1, len(evt.Trades)) + assert.Equal(t, "bid", evt.Trades[0].Side) + assert.Equal(t, "ethtwd", evt.Trades[0].Market) + assert.Equal(t, int64(1521726960357), evt.Trades[0].Timestamp) + assert.Equal(t, "3.2", evt.Trades[0].Fee) + assert.Equal(t, "twd", evt.Trades[0].FeeCurrency) +} From 9c20215f41d39abcfabdc1d8070e372db31c686e Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 24 Jul 2023 15:00:03 +0800 Subject: [PATCH 5/5] max: use fixedpoint.Value for field parsing --- pkg/exchange/max/convert.go | 25 +++--------------- pkg/exchange/max/maxapi/userdata.go | 32 ++++++------------------ pkg/exchange/max/maxapi/userdata_test.go | 2 +- 3 files changed, 13 insertions(+), 46 deletions(-) diff --git a/pkg/exchange/max/convert.go b/pkg/exchange/max/convert.go index 6047f5ee6..48b7687f7 100644 --- a/pkg/exchange/max/convert.go +++ b/pkg/exchange/max/convert.go @@ -288,36 +288,19 @@ func convertWebSocketTrade(t max.TradeUpdate) (*types.Trade, error) { // trade time mts := time.Unix(0, t.Timestamp*int64(time.Millisecond)) - price, err := fixedpoint.NewFromString(t.Price) - if err != nil { - return nil, err - } - - quantity, err := fixedpoint.NewFromString(t.Volume) - if err != nil { - return nil, err - } - - quoteQuantity := price.Mul(quantity) - - fee, err := fixedpoint.NewFromString(t.Fee) - if err != nil { - return nil, err - } - return &types.Trade{ ID: t.ID, OrderID: t.OrderID, Symbol: toGlobalSymbol(t.Market), Exchange: types.ExchangeMax, - Price: price, - Quantity: quantity, + Price: t.Price, + Quantity: t.Volume, Side: side, IsBuyer: side == types.SideTypeBuy, IsMaker: t.Maker, - Fee: fee, + Fee: t.Fee, FeeCurrency: toGlobalCurrency(t.FeeCurrency), - QuoteQuantity: quoteQuantity, + QuoteQuantity: t.Price.Mul(t.Volume), Time: types.Time(mts), }, nil } diff --git a/pkg/exchange/max/maxapi/userdata.go b/pkg/exchange/max/maxapi/userdata.go index 000e65946..26ff6cf4b 100644 --- a/pkg/exchange/max/maxapi/userdata.go +++ b/pkg/exchange/max/maxapi/userdata.go @@ -94,15 +94,15 @@ func parserOrderSnapshotEvent(v *fastjson.Value) *OrderSnapshotEvent { } type TradeUpdate struct { - ID uint64 `json:"i"` - Side string `json:"sd"` - Price string `json:"p"` - Volume string `json:"v"` - Market string `json:"M"` + ID uint64 `json:"i"` + Side string `json:"sd"` + Price fixedpoint.Value `json:"p"` + Volume fixedpoint.Value `json:"v"` + Market string `json:"M"` - Fee string `json:"f"` - FeeCurrency string `json:"fc"` - FeeDiscounted bool `json:"fd"` + Fee fixedpoint.Value `json:"f"` + FeeCurrency string `json:"fc"` + FeeDiscounted bool `json:"fd"` Timestamp int64 `json:"T"` UpdateTime int64 `json:"TU"` @@ -112,22 +112,6 @@ type TradeUpdate struct { Maker bool `json:"m"` } -func parseTradeUpdate(v *fastjson.Value) TradeUpdate { - return TradeUpdate{ - ID: v.GetUint64("i"), - Side: string(v.GetStringBytes("sd")), - Price: string(v.GetStringBytes("p")), - Volume: string(v.GetStringBytes("v")), - Market: string(v.GetStringBytes("M")), - Fee: string(v.GetStringBytes("f")), - FeeCurrency: string(v.GetStringBytes("fc")), - Timestamp: v.GetInt64("T"), - UpdateTime: v.GetInt64("TU"), - OrderID: v.GetUint64("oi"), - Maker: v.GetBool("m"), - } -} - type TradeUpdateEvent struct { BaseEvent Trades []TradeUpdate `json:"t"` diff --git a/pkg/exchange/max/maxapi/userdata_test.go b/pkg/exchange/max/maxapi/userdata_test.go index af80dcd22..2967e16df 100644 --- a/pkg/exchange/max/maxapi/userdata_test.go +++ b/pkg/exchange/max/maxapi/userdata_test.go @@ -40,6 +40,6 @@ func Test_parseTradeSnapshotEvent(t *testing.T) { assert.Equal(t, "bid", evt.Trades[0].Side) assert.Equal(t, "ethtwd", evt.Trades[0].Market) assert.Equal(t, int64(1521726960357), evt.Trades[0].Timestamp) - assert.Equal(t, "3.2", evt.Trades[0].Fee) + assert.Equal(t, "3.2", evt.Trades[0].Fee.String()) assert.Equal(t, "twd", evt.Trades[0].FeeCurrency) }