kucoin: fix spot order unmarshal bug (#1380)

* kucoin: fix unmarshal bug

* kucoin: Add time in force handling

* kucoin: fix test

* thrasher: nits

* kucoin_test: shift skip check down for coverage, rm market testing, change buy price to reduce chance of instant match

* kucoin: fix nits and force usage of uuid to rm conflicts

---------

Co-authored-by: shazbert <ryan.oharareid@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2023-11-14 15:07:35 +11:00
committed by GitHub
parent a13a6c3248
commit f65a582cd6
4 changed files with 49 additions and 27 deletions

View File

@@ -815,6 +815,7 @@ func (ku *Kucoin) GetServiceStatus(ctx context.Context) (*ServiceStatus, error)
// Note: use this only for SPOT trades
func (ku *Kucoin) PostOrder(ctx context.Context, arg *SpotOrderParam) (string, error) {
if arg.ClientOrderID == "" {
// NOTE: 128 bit max length character string. UUID recommended.
return "", errInvalidClientOrderID
}
if arg.Side == "" {
@@ -841,11 +842,13 @@ func (ku *Kucoin) PostOrder(ctx context.Context, arg *SpotOrderParam) (string, e
default:
return "", fmt.Errorf("%w %s", order.ErrTypeIsInvalid, arg.OrderType)
}
resp := struct {
OrderID string `json:"orderId"`
var resp struct {
Data struct {
OrderID string `json:"orderId"`
} `json:"data"`
Error
}{}
return resp.OrderID, ku.SendAuthHTTPRequest(ctx, exchange.RestSpot, placeOrderEPL, http.MethodPost, kucoinPostOrder, &arg, &resp)
}
return resp.Data.OrderID, ku.SendAuthHTTPRequest(ctx, exchange.RestSpot, placeOrderEPL, http.MethodPost, kucoinPostOrder, &arg, &resp)
}
// PostMarginOrder used to place two types of margin orders: limit and market

View File

@@ -9,6 +9,7 @@ import (
"testing"
"time"
"github.com/gofrs/uuid"
"github.com/stretchr/testify/assert"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
@@ -563,7 +564,6 @@ func TestGetServiceStatus(t *testing.T) {
func TestPostOrder(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, ku, canManipulateRealOrders)
// default order type is limit
_, err := ku.PostOrder(context.Background(), &SpotOrderParam{
@@ -571,43 +571,46 @@ func TestPostOrder(t *testing.T) {
if !errors.Is(err, errInvalidClientOrderID) {
t.Errorf("PostOrder() expected %v, but found %v", errInvalidClientOrderID, err)
}
customID, err := uuid.NewV4()
if err != nil {
t.Fatal(err)
}
_, err = ku.PostOrder(context.Background(), &SpotOrderParam{
ClientOrderID: "5bd6e9286d99522a52e458de", Symbol: spotTradablePair,
ClientOrderID: customID.String(), Symbol: spotTradablePair,
OrderType: ""})
if !errors.Is(err, order.ErrSideIsInvalid) {
t.Errorf("PostOrder() expected %v, but found %v", order.ErrSideIsInvalid, err)
}
_, err = ku.PostOrder(context.Background(), &SpotOrderParam{
ClientOrderID: "5bd6e9286d99522a52e458de", Symbol: currency.EMPTYPAIR,
ClientOrderID: customID.String(), Symbol: currency.EMPTYPAIR,
Size: 0.1, Side: "buy", Price: 234565})
if !errors.Is(err, currency.ErrCurrencyPairEmpty) {
t.Errorf("PostOrder() expected %v, but found %v", currency.ErrCurrencyPairEmpty, err)
}
_, err = ku.PostOrder(context.Background(), &SpotOrderParam{
ClientOrderID: "5bd6e9286d99522a52e458de", Side: "buy",
ClientOrderID: customID.String(), Side: "buy",
Symbol: spotTradablePair,
OrderType: "limit", Size: 0.1})
if !errors.Is(err, errInvalidPrice) {
t.Errorf("PostOrder() expected %v, but found %v", errInvalidPrice, err)
}
_, err = ku.PostOrder(context.Background(), &SpotOrderParam{
ClientOrderID: "5bd6e9286d99522a52e458de", Symbol: spotTradablePair, Side: "buy",
ClientOrderID: customID.String(), Symbol: spotTradablePair, Side: "buy",
OrderType: "limit", Price: 234565})
if !errors.Is(err, errInvalidSize) {
t.Errorf("PostOrder() expected %v, but found %v", errInvalidSize, err)
}
_, err = ku.PostOrder(context.Background(), &SpotOrderParam{
ClientOrderID: "5bd6e9286d99522a52e458de", Side: "buy",
Symbol: spotTradablePair, OrderType: "limit", Size: 0.1, Price: 234565})
if err != nil {
t.Error("PostOrder() error", err)
}
// market order
sharedtestvalues.SkipTestIfCredentialsUnset(t, ku, canManipulateRealOrders)
_, err = ku.PostOrder(context.Background(), &SpotOrderParam{
ClientOrderID: "5bd6e9286d99522a52e458de", Side: "buy",
Symbol: spotTradablePair,
OrderType: "market", Remark: "remark", Size: 0.1})
ClientOrderID: customID.String(),
Side: "buy",
Symbol: spotTradablePair,
OrderType: "limit",
Size: 0.005,
Price: 1000})
if err != nil {
t.Error("PostOrder() error", err)
}

View File

@@ -32,7 +32,7 @@ var (
errMissingOrderbookSequence = errors.New("missing orderbook sequence")
errSizeOrFundIsRequired = errors.New("at least one required among size and funds")
errInvalidLeverage = errors.New("invalid leverage value")
errInvalidClientOrderID = errors.New("invalid client order ID")
errInvalidClientOrderID = errors.New("no client order ID supplied, this endpoint requires a UUID or similar string")
subAccountRegExp = regexp.MustCompile("^[a-zA-Z0-9]{7-32}$")
subAccountPassphraseRegExp = regexp.MustCompile("^[a-zA-Z0-9]{7-24}$")

View File

@@ -761,13 +761,29 @@ func (ku *Kucoin) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Subm
}
return s.DeriveSubmitResponse(o)
case asset.Spot:
if s.ClientID != "" && s.ClientOrderID == "" {
s.ClientOrderID = s.ClientID
timeInForce := ""
if s.Type == order.Limit {
switch {
case s.FillOrKill:
timeInForce = "FOK"
case s.ImmediateOrCancel:
timeInForce = "IOC"
case s.PostOnly:
default:
timeInForce = "GTC"
}
}
o, err := ku.PostOrder(ctx, &SpotOrderParam{
ClientOrderID: s.ClientOrderID, Side: sideString,
Symbol: s.Pair, OrderType: s.Type.Lower(), Size: s.Amount,
Price: s.Price, PostOnly: s.PostOnly, Hidden: s.Hidden})
ClientOrderID: s.ClientOrderID,
Side: sideString,
Symbol: s.Pair,
OrderType: s.Type.Lower(),
Size: s.Amount,
Price: s.Price,
PostOnly: s.PostOnly,
Hidden: s.Hidden,
TimeInForce: timeInForce,
})
if err != nil {
return nil, err
}
@@ -1936,7 +1952,7 @@ func (ku *Kucoin) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item)
continue
}
pair, enabled, err := ku.MatchSymbolCheckEnabled(symbols[x].Symbol, a, true)
if err != nil {
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
return err
}
if !enabled {
@@ -1962,7 +1978,7 @@ func (ku *Kucoin) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item)
limits = make([]order.MinMaxLevel, 0, len(contract))
for x := range contract {
pair, enabled, err := ku.MatchSymbolCheckEnabled(contract[x].Symbol, a, false)
if err != nil {
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
return err
}
if !enabled {