Merge pull request #364 from austin362667/query-trades

binance: read orders & trades and add related conversions
This commit is contained in:
Yo-An Lin 2021-12-15 01:04:55 +08:00 committed by GitHub
commit 592059fc73
4 changed files with 163 additions and 36 deletions

3
go.mod
View File

@ -31,7 +31,6 @@ require (
github.com/magiconair/properties v1.8.4 // indirect
github.com/mattn/go-colorable v0.1.7 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pkg/errors v0.9.1
@ -55,7 +54,7 @@ require (
github.com/x-cray/logrus-prefixed-formatter v0.5.2
github.com/zserge/lorca v0.1.9
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/multierr v1.7.0
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/net v0.0.0-20211205041911-012df41ee64c // indirect
golang.org/x/sys v0.0.0-20211204120058-94396e421777 // indirect

18
go.sum
View File

@ -96,7 +96,6 @@ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7a
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-redis/redis/v8 v8.8.0 h1:fDZP58UN/1RD3DjtTXP/fFZ04TFohSYhjZDkcDe2dnw=
github.com/go-redis/redis/v8 v8.8.0/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@ -186,10 +185,6 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jmoiron/sqlx v1.3.0 h1:xOXsPZ1cwOn1bhi0p6HzHGkLZicSun/jBtY/YuUuQs8=
github.com/jmoiron/sqlx v1.3.0/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w=
github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
@ -250,7 +245,6 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@ -258,9 +252,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@ -461,9 +452,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211205041911-012df41ee64c h1:7SfqwP5fxEtl/P02w5IhKc86ziJ+A25yFrkVgoy2FT8=
golang.org/x/net v0.0.0-20211205041911-012df41ee64c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -475,7 +463,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -501,9 +488,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210217090653-ed5674b6da4a h1:m4knbKtdWq+rPB3TE+ApaRzkETZngkKdhYjvTnnRq4s=
golang.org/x/sys v0.0.0-20210217090653-ed5674b6da4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211204120058-94396e421777 h1:QAkhGVjOxMa+n4mlsAWeAU+BMZmimQAaNiMu+iUi94E=
golang.org/x/sys v0.0.0-20211204120058-94396e421777/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -514,8 +498,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -312,6 +312,58 @@ func toGlobalTrade(t binance.TradeV3, isMargin bool) (*types.Trade, error) {
}, nil
}
func toGlobalFuturesTrade(t futures.AccountTrade) (*types.Trade, error) {
// skip trade ID that is the same. however this should not happen
var side types.SideType
if t.Buyer {
side = types.SideTypeBuy
} else {
side = types.SideTypeSell
}
price, err := strconv.ParseFloat(t.Price, 64)
if err != nil {
return nil, errors.Wrapf(err, "price parse error, price: %+v", t.Price)
}
quantity, err := strconv.ParseFloat(t.Quantity, 64)
if err != nil {
return nil, errors.Wrapf(err, "quantity parse error, quantity: %+v", t.Quantity)
}
var quoteQuantity = 0.0
if len(t.QuoteQuantity) > 0 {
quoteQuantity, err = strconv.ParseFloat(t.QuoteQuantity, 64)
if err != nil {
return nil, errors.Wrapf(err, "quote quantity parse error, quoteQuantity: %+v", t.QuoteQuantity)
}
} else {
quoteQuantity = price * quantity
}
fee, err := strconv.ParseFloat(t.Commission, 64)
if err != nil {
return nil, errors.Wrapf(err, "commission parse error, commission: %+v", t.Commission)
}
return &types.Trade{
ID: t.ID,
OrderID: uint64(t.OrderID),
Price: price,
Symbol: t.Symbol,
Exchange: "binance",
Quantity: quantity,
QuoteQuantity: quoteQuantity,
Side: side,
IsBuyer: t.Buyer,
IsMaker: t.Maker,
Fee: fee,
FeeCurrency: t.CommissionAsset,
Time: types.Time(millisecondTime(t.Time)),
IsFutures: true,
}, nil
}
func toGlobalSideType(side binance.SideType) types.SideType {
switch side {
case binance.SideTypeBuy:

View File

@ -10,6 +10,8 @@ import (
"strings"
"time"
"go.uber.org/multierr"
"golang.org/x/time/rate"
"github.com/adshao/go-binance/v2"
@ -431,6 +433,17 @@ func (e *Exchange) QueryOpenOrders(ctx context.Context, symbol string) (orders [
return toGlobalOrders(binanceOrders)
}
if e.IsFutures {
req := e.futuresClient.NewListOpenOrdersService().Symbol(symbol)
binanceOrders, err := req.Do(ctx)
if err != nil {
return orders, err
}
return toGlobalFuturesOrders(binanceOrders)
}
binanceOrders, err := e.Client.NewListOpenOrdersService().Symbol(symbol).Do(ctx)
if err != nil {
return orders, err
@ -465,6 +478,24 @@ func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since,
return toGlobalOrders(binanceOrders)
}
if e.IsFutures {
req := e.futuresClient.NewListOrdersService().Symbol(symbol)
if lastOrderID > 0 {
req.OrderID(int64(lastOrderID))
} else {
req.StartTime(since.UnixNano() / int64(time.Millisecond)).
EndTime(until.UnixNano() / int64(time.Millisecond))
}
binanceOrders, err := req.Do(ctx)
if err != nil {
return orders, err
}
return toGlobalFuturesOrders(binanceOrders)
}
req := e.Client.NewListOrdersService().
Symbol(symbol)
@ -483,7 +514,28 @@ func (e *Exchange) QueryClosedOrders(ctx context.Context, symbol string, since,
return toGlobalOrders(binanceOrders)
}
func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err2 error) {
func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err error) {
if e.IsFutures {
for _, o := range orders {
var req = e.futuresClient.NewCancelOrderService()
// Mandatory
req.Symbol(o.Symbol)
if o.OrderID > 0 {
req.OrderID(int64(o.OrderID))
}
_, _err := req.Do(ctx)
if _err != nil {
log.WithError(_err).Errorf("order cancel error")
err = multierr.Append(err, _err)
}
}
return err
}
for _, o := range orders {
var req = e.Client.NewCancelOrderService()
@ -496,14 +548,14 @@ func (e *Exchange) CancelOrders(ctx context.Context, orders ...types.Order) (err
req.NewClientOrderID(o.ClientOrderID)
}
_, err := req.Do(ctx)
if err != nil {
log.WithError(err).Errorf("order cancel error")
err2 = err
_, _err := req.Do(ctx)
if _err != nil {
log.WithError(_err).Errorf("order cancel error")
err = multierr.Append(err, _err)
}
}
return err2
return err
}
func (e *Exchange) submitMarginOrder(ctx context.Context, order types.SubmitOrder) (*types.Order, error) {
@ -886,9 +938,9 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval type
}
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *types.TradeQueryOptions) (trades []types.Trade, err error) {
var remoteTrades []*binance.TradeV3
if e.IsMargin {
var remoteTrades []*binance.TradeV3
req := e.Client.NewListMarginTradesService().
IsIsolated(e.IsIsolatedMargin).
Symbol(symbol)
@ -916,7 +968,50 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
if err != nil {
return nil, err
}
for _, t := range remoteTrades {
localTrade, err := toGlobalTrade(*t, e.IsMargin)
if err != nil {
log.WithError(err).Errorf("can not convert binance trade: %+v", t)
continue
}
trades = append(trades, *localTrade)
}
return trades, nil
} else if e.IsFutures {
var remoteTrades []*futures.AccountTrade
req := e.futuresClient.NewListAccountTradeService(). // IsIsolated(e.IsIsolatedFutures).
Symbol(symbol)
if options.Limit > 0 {
req.Limit(int(options.Limit))
} else {
req.Limit(1000)
}
// BINANCE uses inclusive last trade ID
if options.LastTradeID > 0 {
req.FromID(options.LastTradeID)
}
remoteTrades, err = req.Do(ctx)
if err != nil {
return nil, err
}
for _, t := range remoteTrades {
localTrade, err := toGlobalFuturesTrade(*t)
if err != nil {
log.WithError(err).Errorf("can not convert binance trade: %+v", t)
continue
}
trades = append(trades, *localTrade)
}
return trades, nil
} else {
var remoteTrades []*binance.TradeV3
req := e.Client.NewListTradesService().
Symbol(symbol)
@ -942,19 +1037,18 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, options *type
if err != nil {
return nil, err
}
}
for _, t := range remoteTrades {
localTrade, err := toGlobalTrade(*t, e.IsMargin)
if err != nil {
log.WithError(err).Errorf("can not convert binance trade: %+v", t)
continue
}
for _, t := range remoteTrades {
localTrade, err := toGlobalTrade(*t, e.IsMargin)
if err != nil {
log.WithError(err).Errorf("can not convert binance trade: %+v", t)
continue
trades = append(trades, *localTrade)
}
trades = append(trades, *localTrade)
return trades, nil
}
return trades, nil
}
func (e *Exchange) BatchQueryKLines(ctx context.Context, symbol string, interval types.Interval, startTime, endTime time.Time) ([]types.KLine, error) {