Merge remote-tracking branch 'origin/v1.50'

This commit is contained in:
c9s 2023-07-24 17:02:08 +08:00
commit afc5dbb951
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
6 changed files with 159 additions and 86 deletions

View File

@ -4,7 +4,9 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
branches:
- "main"
- "v*"
jobs:
build:

View File

@ -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
}

View File

@ -94,74 +94,46 @@ 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"`
Timestamp int64 `json:"T"`
UpdateTime int64 `json:"TU"`
Fee fixedpoint.Value `json:"f"`
FeeCurrency string `json:"fc"`
FeeDiscounted bool `json:"fd"`
Timestamp int64 `json:"T"`
UpdateTime int64 `json:"TU"`
OrderID uint64 `json:"oi"`
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"`
}
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 +224,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)

View File

@ -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.String())
assert.Equal(t, "twd", evt.Trades[0].FeeCurrency)
}

View File

@ -35,28 +35,48 @@ 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
}
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 +114,7 @@ func (g *Grid) CalculateGeometricPins() {
return nil
}
g.addPins(g.calculator())
g.addPins(removeDuplicatedPins(g.calculator()))
}
func (g *Grid) CalculateArithmeticPins() {

View File

@ -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)
}