mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-09 07:26:48 +00:00
exchanges/order: Add TimeInForce type (#1382)
* Added TimeInForce type and updated related files * Linter issue fix and minor coinbasepro type update * Bitrex consts update * added unit test and minor changes in bittrex * Unit tests update * Fix minor linter issues * Update TestStringToTimeInForce unit test * fix conflict with gateio timeInForce * Update order tests * Complete updating the order unit tests * update kucoin and deribit wrapper to match the time in force change * fix time-in-force related test errors * linter issue fix * time in force constants, functions and unit tests update * shift tif policies to TimeInForce * Update time-in-force, related functions, and unit tests * fix linter issue and time-in-force processing * added a good till crossing tif value * order type fix and fix related tim-in-force entries * update time-in-force unmarshaling and unit test * fix time-in-force error in gateio * linter issue fix * update based on review comments * add unit test and fix missing issues * minor fix and added benchmark unit test * change GTT to GTC for limit * fix linter issue * added time-in-force value to place order param * fix minor issues based on review comment and move tif code to separate files * update on exchanges linked to time-in-force * resolve missing review comments * minor linter issues fix * added time-in-force handler and update timeInForce parametered endpoint * minor fixes based on review * nits fix * update based on review * linter fix * rm getTimeInForce func and minor change to time-in-force * minor change * update based on review comments * wrappers and time-in-force calling approach * minor change * update gateio string to timeInForce conversion and unit test * updated order test unit tes functions * minor fixes on unit tests * nits fix based on feedback * update TestDeriveCancel unit test assert messages * update TestDeriveCancel unit test assert messages * update timeInForceFromString method to return formatted error and update functions using it * restructure and fix minor exchanges time-in-force handling issues * replaced unused getTypeFromTimeInForce with inline switch-based order type check * separated the repeated timeInForce conversion code to a function * update exchanges time-in-force handling based on review comments * limter fix * edded comment to validTimesInForce var * added comment to gateio's timeInForceString func * added goodTillCancel switch case to gateio timeInForceString func
This commit is contained in:
@@ -152,7 +152,6 @@ var (
|
||||
errInvalidLeverageValue = errors.New("invalid leverage value")
|
||||
errInvalidRiskLimit = errors.New("new position risk limit")
|
||||
errInvalidCountTotalValue = errors.New("invalid \"count_total\" value, supported \"count_total\" values are 0 and 1")
|
||||
errInvalidTimeInForce = errors.New("invalid time in force value")
|
||||
errInvalidAutoSizeValue = errors.New("invalid \"auto_size\" value, only \"close_long\" and \"close_short\" are supported")
|
||||
errTooManyOrderRequest = errors.New("too many order creation request")
|
||||
errInvalidTimeout = errors.New("invalid timeout, should be in seconds At least 5 seconds, 0 means cancel the countdown")
|
||||
@@ -168,6 +167,24 @@ var (
|
||||
errInvalidTextValue = errors.New("invalid text value, requires prefix `t-`")
|
||||
)
|
||||
|
||||
// validTimesInForce holds a list of supported time-in-force values and corresponding string representations.
|
||||
// slice iteration outperforms map with this few elements
|
||||
var validTimesInForce = []struct {
|
||||
String string
|
||||
TimeInForce order.TimeInForce
|
||||
}{
|
||||
{gtcTIF, order.GoodTillCancel}, {iocTIF, order.ImmediateOrCancel}, {pocTIF, order.PostOnly}, {fokTIF, order.FillOrKill},
|
||||
}
|
||||
|
||||
func timeInForceFromString(tif string) (order.TimeInForce, error) {
|
||||
for a := range validTimesInForce {
|
||||
if validTimesInForce[a].String == tif {
|
||||
return validTimesInForce[a].TimeInForce, nil
|
||||
}
|
||||
}
|
||||
return order.UnknownTIF, fmt.Errorf("%w: %q", order.ErrUnsupportedTimeInForce, tif)
|
||||
}
|
||||
|
||||
// Gateio is the overarching type across this package
|
||||
type Gateio struct {
|
||||
Counter common.Counter // Must be first due to alignment requirements
|
||||
@@ -829,7 +846,7 @@ func (g *Gateio) CreatePriceTriggeredOrder(ctx context.Context, arg *PriceTrigge
|
||||
return nil, errNilArgument
|
||||
}
|
||||
if arg.Put.TimeInForce != gtcTIF && arg.Put.TimeInForce != iocTIF {
|
||||
return nil, fmt.Errorf("%w, only 'gct' and 'ioc' are supported", errInvalidTimeInForce)
|
||||
return nil, fmt.Errorf("%w: %q only 'gct' and 'ioc' are supported", order.ErrUnsupportedTimeInForce, arg.Put.TimeInForce)
|
||||
}
|
||||
if arg.Market.IsEmpty() {
|
||||
return nil, fmt.Errorf("%w, %s", currency.ErrCurrencyPairEmpty, "field market is required")
|
||||
@@ -2294,14 +2311,14 @@ func (g *Gateio) PlaceFuturesOrder(ctx context.Context, arg *ContractOrderCreate
|
||||
if arg.Size == 0 {
|
||||
return nil, fmt.Errorf("%w, specify positive number to make a bid, and negative number to ask", order.ErrSideIsInvalid)
|
||||
}
|
||||
if arg.TimeInForce != gtcTIF && arg.TimeInForce != iocTIF && arg.TimeInForce != pocTIF && arg.TimeInForce != fokTIF {
|
||||
return nil, errInvalidTimeInForce
|
||||
if _, err := timeInForceFromString(arg.TimeInForce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if arg.Price == "" {
|
||||
return nil, errInvalidPrice
|
||||
}
|
||||
if arg.Price == "0" && arg.TimeInForce != iocTIF && arg.TimeInForce != fokTIF {
|
||||
return nil, errInvalidTimeInForce
|
||||
return nil, fmt.Errorf("%w: %q; only 'IOC' and 'FOK' allowed for market order", order.ErrUnsupportedTimeInForce, arg.TimeInForce)
|
||||
}
|
||||
if arg.AutoSize != "" && (arg.AutoSize == "close_long" || arg.AutoSize == "close_short") {
|
||||
return nil, errInvalidAutoSizeValue
|
||||
@@ -2383,17 +2400,14 @@ func (g *Gateio) PlaceBatchFuturesOrders(ctx context.Context, settle currency.Co
|
||||
if args[x].Size == 0 {
|
||||
return nil, fmt.Errorf("%w, specify positive number to make a bid, and negative number to ask", order.ErrSideIsInvalid)
|
||||
}
|
||||
if args[x].TimeInForce != gtcTIF &&
|
||||
args[x].TimeInForce != iocTIF &&
|
||||
args[x].TimeInForce != pocTIF &&
|
||||
args[x].TimeInForce != fokTIF {
|
||||
return nil, errInvalidTimeInForce
|
||||
if _, err := timeInForceFromString(args[x].TimeInForce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if args[x].Price == "" {
|
||||
return nil, errInvalidPrice
|
||||
}
|
||||
if args[x].Price == "0" && args[x].TimeInForce != iocTIF && args[x].TimeInForce != fokTIF {
|
||||
return nil, errInvalidTimeInForce
|
||||
return nil, fmt.Errorf("%w: %q; only 'ioc' and 'fok' allowed for market order", order.ErrUnsupportedTimeInForce, args[x].TimeInForce)
|
||||
}
|
||||
if args[x].Text != "" && !strings.HasPrefix(args[x].Text, "t-") {
|
||||
return nil, errInvalidTextValue
|
||||
@@ -2547,7 +2561,7 @@ func (g *Gateio) CreatePriceTriggeredFuturesOrder(ctx context.Context, settle cu
|
||||
return nil, fmt.Errorf("%w, price must be greater than 0", errInvalidPrice)
|
||||
}
|
||||
if arg.Initial.TimeInForce != "" && arg.Initial.TimeInForce != gtcTIF && arg.Initial.TimeInForce != iocTIF {
|
||||
return nil, fmt.Errorf("%w, only time in force value 'gtc' and 'ioc' are supported", errInvalidTimeInForce)
|
||||
return nil, fmt.Errorf("%w: %q; only 'gtc' and 'ioc' are allowed", order.ErrInvalidTimeInForce, arg.Initial.TimeInForce)
|
||||
}
|
||||
if arg.Trigger.StrategyType != 0 && arg.Trigger.StrategyType != 1 {
|
||||
return nil, errors.New("strategy type must be 0 or 1, 0: by price, and 1: by price gap")
|
||||
@@ -2870,8 +2884,8 @@ func (g *Gateio) PlaceDeliveryOrder(ctx context.Context, arg *ContractOrderCreat
|
||||
if arg.Size == 0 {
|
||||
return nil, fmt.Errorf("%w, specify positive number to make a bid, and negative number to ask", order.ErrSideIsInvalid)
|
||||
}
|
||||
if arg.TimeInForce != gtcTIF && arg.TimeInForce != iocTIF && arg.TimeInForce != pocTIF && arg.TimeInForce != fokTIF {
|
||||
return nil, errInvalidTimeInForce
|
||||
if _, err := timeInForceFromString(arg.TimeInForce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if arg.Price == "" {
|
||||
return nil, errInvalidPrice
|
||||
@@ -3071,7 +3085,7 @@ func (g *Gateio) GetDeliveryPriceTriggeredOrder(ctx context.Context, settle curr
|
||||
}
|
||||
if arg.Initial.TimeInForce != "" &&
|
||||
arg.Initial.TimeInForce != gtcTIF && arg.Initial.TimeInForce != iocTIF {
|
||||
return nil, fmt.Errorf("%w, only time in force value 'gtc' and 'ioc' are supported", errInvalidTimeInForce)
|
||||
return nil, fmt.Errorf("%w: %q; only 'gtc' and 'ioc' are allowed", order.ErrUnsupportedTimeInForce, arg.Initial.TimeInForce)
|
||||
}
|
||||
if arg.Trigger.StrategyType != 0 && arg.Trigger.StrategyType != 1 {
|
||||
return nil, errors.New("strategy type must be 0 or 1, 0: by price, and 1: by price gap")
|
||||
|
||||
@@ -1816,13 +1816,14 @@ func TestSubmitOrder(t *testing.T) {
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, g, canManipulateRealOrders)
|
||||
for _, a := range g.GetAssetTypes(false) {
|
||||
_, err := g.SubmitOrder(t.Context(), &order.Submit{
|
||||
Exchange: g.Name,
|
||||
Pair: getPair(t, a),
|
||||
Side: order.Buy,
|
||||
Type: order.Limit,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
AssetType: a,
|
||||
Exchange: g.Name,
|
||||
Pair: getPair(t, a),
|
||||
Side: order.Buy,
|
||||
Type: order.Limit,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
AssetType: a,
|
||||
TimeInForce: order.GoodTillCancel,
|
||||
})
|
||||
assert.NoErrorf(t, err, "SubmitOrder should not error for %s", a)
|
||||
}
|
||||
@@ -2738,25 +2739,6 @@ func TestGetClientOrderIDFromText(t *testing.T) {
|
||||
assert.Equal(t, "t-123", getClientOrderIDFromText("t-123"), "should return t-123")
|
||||
}
|
||||
|
||||
func TestGetTypeFromTimeInForce(t *testing.T) {
|
||||
t.Parallel()
|
||||
typeResp, postOnly := getTypeFromTimeInForce("gtc")
|
||||
assert.Equal(t, order.Limit, typeResp, "should be a limit order")
|
||||
assert.False(t, postOnly, "should return false")
|
||||
|
||||
typeResp, postOnly = getTypeFromTimeInForce("ioc")
|
||||
assert.Equal(t, order.Market, typeResp, "should be market order")
|
||||
assert.False(t, postOnly, "should return false")
|
||||
|
||||
typeResp, postOnly = getTypeFromTimeInForce("poc")
|
||||
assert.Equal(t, order.Limit, typeResp, "should be limit order")
|
||||
assert.True(t, postOnly, "should return true")
|
||||
|
||||
typeResp, postOnly = getTypeFromTimeInForce("fok")
|
||||
assert.Equal(t, order.Market, typeResp, "should be market order")
|
||||
assert.False(t, postOnly, "should return false")
|
||||
}
|
||||
|
||||
func TestGetSideAndAmountFromSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
side, amount, remaining := getSideAndAmountFromSize(1, 1)
|
||||
@@ -2784,29 +2766,6 @@ func TestGetFutureOrderSize(t *testing.T) {
|
||||
assert.Equal(t, -1.0, ret)
|
||||
}
|
||||
|
||||
func TestGetTimeInForce(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := getTimeInForce(&order.Submit{Type: order.Market, PostOnly: true})
|
||||
assert.ErrorIs(t, err, errPostOnlyOrderTypeUnsupported)
|
||||
|
||||
ret, err := getTimeInForce(&order.Submit{Type: order.Market})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "ioc", ret)
|
||||
|
||||
ret, err = getTimeInForce(&order.Submit{Type: order.Limit, PostOnly: true})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "poc", ret)
|
||||
|
||||
ret, err = getTimeInForce(&order.Submit{Type: order.Limit})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "gtc", ret)
|
||||
|
||||
ret, err = getTimeInForce(&order.Submit{Type: order.Market, FillOrKill: true})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "fok", ret)
|
||||
}
|
||||
|
||||
func TestProcessFuturesOrdersPushData(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := []struct {
|
||||
@@ -2967,7 +2926,7 @@ func TestDeriveSpotWebsocketOrderResponse(t *testing.T) {
|
||||
Type: order.Market,
|
||||
Side: order.Sell,
|
||||
Status: order.Filled,
|
||||
ImmediateOrCancel: true,
|
||||
TimeInForce: order.ImmediateOrCancel,
|
||||
Cost: 0.0001,
|
||||
Purchased: 9.35033,
|
||||
}, got)
|
||||
@@ -3010,7 +2969,7 @@ func TestDeriveSpotWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Market,
|
||||
Side: order.Sell,
|
||||
Status: order.Filled,
|
||||
ImmediateOrCancel: true,
|
||||
TimeInForce: order.ImmediateOrCancel,
|
||||
Cost: 0.0001,
|
||||
Purchased: 9.35033,
|
||||
},
|
||||
@@ -3028,7 +2987,7 @@ func TestDeriveSpotWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Market,
|
||||
Side: order.Buy,
|
||||
Status: order.Filled,
|
||||
ImmediateOrCancel: true,
|
||||
TimeInForce: order.ImmediateOrCancel,
|
||||
Cost: 9.991512,
|
||||
Purchased: 816.3,
|
||||
},
|
||||
@@ -3046,7 +3005,7 @@ func TestDeriveSpotWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Limit,
|
||||
Side: order.Buy,
|
||||
Status: order.Filled,
|
||||
FillOrKill: true,
|
||||
TimeInForce: order.FillOrKill,
|
||||
Cost: 7.346,
|
||||
Purchased: 200,
|
||||
},
|
||||
@@ -3064,7 +3023,7 @@ func TestDeriveSpotWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Limit,
|
||||
Side: order.Buy,
|
||||
Status: order.Open,
|
||||
PostOnly: true,
|
||||
TimeInForce: order.PostOnly,
|
||||
},
|
||||
{
|
||||
Exchange: g.Name,
|
||||
@@ -3080,6 +3039,7 @@ func TestDeriveSpotWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Limit,
|
||||
Side: order.Sell,
|
||||
Status: order.Open,
|
||||
TimeInForce: order.GoodTillCancel,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3127,7 +3087,7 @@ func TestDeriveFuturesWebsocketOrderResponse(t *testing.T) {
|
||||
Type: order.Market,
|
||||
Side: order.Long,
|
||||
Status: order.Filled,
|
||||
ImmediateOrCancel: true,
|
||||
TimeInForce: order.ImmediateOrCancel,
|
||||
ReduceOnly: true,
|
||||
}, got)
|
||||
}
|
||||
@@ -3170,7 +3130,7 @@ func TestDeriveFuturesWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Market,
|
||||
Side: order.Long,
|
||||
Status: order.Filled,
|
||||
ImmediateOrCancel: true,
|
||||
TimeInForce: order.ImmediateOrCancel,
|
||||
ReduceOnly: true,
|
||||
},
|
||||
{
|
||||
@@ -3186,7 +3146,7 @@ func TestDeriveFuturesWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Market,
|
||||
Side: order.Short,
|
||||
Status: order.Filled,
|
||||
ImmediateOrCancel: true,
|
||||
TimeInForce: order.ImmediateOrCancel,
|
||||
},
|
||||
{
|
||||
Exchange: g.Name,
|
||||
@@ -3201,6 +3161,7 @@ func TestDeriveFuturesWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Limit,
|
||||
Side: order.Long,
|
||||
Status: order.Open,
|
||||
TimeInForce: order.GoodTillCancel,
|
||||
},
|
||||
{
|
||||
Exchange: g.Name,
|
||||
@@ -3215,6 +3176,7 @@ func TestDeriveFuturesWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Limit,
|
||||
Side: order.Short,
|
||||
Status: order.Open,
|
||||
TimeInForce: order.GoodTillCancel,
|
||||
},
|
||||
{
|
||||
Exchange: g.Name,
|
||||
@@ -3228,7 +3190,7 @@ func TestDeriveFuturesWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Market,
|
||||
Side: order.Long,
|
||||
Status: order.Filled,
|
||||
ImmediateOrCancel: true,
|
||||
TimeInForce: order.ImmediateOrCancel,
|
||||
},
|
||||
{
|
||||
Exchange: g.Name,
|
||||
@@ -3242,7 +3204,7 @@ func TestDeriveFuturesWebsocketOrderResponses(t *testing.T) {
|
||||
Type: order.Market,
|
||||
Side: order.Short,
|
||||
Status: order.Filled,
|
||||
ImmediateOrCancel: true,
|
||||
TimeInForce: order.ImmediateOrCancel,
|
||||
ReduceOnly: true,
|
||||
},
|
||||
},
|
||||
@@ -3335,3 +3297,51 @@ func getPairs(tb testing.TB, a asset.Item) currency.Pairs {
|
||||
|
||||
return enabledPairs
|
||||
}
|
||||
|
||||
func BenchmarkTimeInForceFromString(b *testing.B) {
|
||||
for b.Loop() {
|
||||
for _, tifString := range []string{gtcTIF, iocTIF, pocTIF, fokTIF} {
|
||||
if _, err := timeInForceFromString(tifString); err != nil {
|
||||
b.Fatal(tifString)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeInForceFromString(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := timeInForceFromString("abcdef")
|
||||
assert.ErrorIs(t, err, order.ErrUnsupportedTimeInForce)
|
||||
|
||||
for k, v := range map[string]order.TimeInForce{gtcTIF: order.GoodTillCancel, iocTIF: order.ImmediateOrCancel, pocTIF: order.PostOnly, fokTIF: order.FillOrKill} {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tif, err := timeInForceFromString(k)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, v, tif)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTypeFromTimeInForce(t *testing.T) {
|
||||
t.Parallel()
|
||||
typeResp := getTypeFromTimeInForce("gtc", 0)
|
||||
assert.Equal(t, order.Limit, typeResp)
|
||||
|
||||
typeResp = getTypeFromTimeInForce("ioc", 0)
|
||||
assert.Equal(t, order.Market, typeResp, "should be market order")
|
||||
|
||||
typeResp = getTypeFromTimeInForce("poc", 123)
|
||||
assert.Equal(t, order.Limit, typeResp, "should be limit order")
|
||||
|
||||
typeResp = getTypeFromTimeInForce("fok", 0)
|
||||
assert.Equal(t, order.Market, typeResp, "should be market order")
|
||||
}
|
||||
|
||||
func TestTimeInForceString(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Empty(t, timeInForceString(order.UnknownTIF))
|
||||
for _, valid := range validTimesInForce {
|
||||
assert.Equal(t, valid.String, timeInForceString(valid.TimeInForce))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -972,10 +972,6 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
timeInForce, err := getTimeInForce(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
settle, err := getSettlementCurrency(s.Pair, s.AssetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -986,7 +982,7 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
|
||||
Price: strconv.FormatFloat(s.Price, 'f', -1, 64), // Cannot be an empty string, requires "0" for market orders.
|
||||
Settle: settle,
|
||||
ReduceOnly: s.ReduceOnly,
|
||||
TimeInForce: timeInForce,
|
||||
TimeInForce: timeInForceString(s.TimeInForce),
|
||||
Text: s.ClientOrderID,
|
||||
}
|
||||
var o *Order
|
||||
@@ -1304,8 +1300,10 @@ func (g *Gateio) GetOrderInfo(ctx context.Context, orderID string, pair currency
|
||||
}
|
||||
|
||||
side, amount, remaining := getSideAndAmountFromSize(fOrder.Size, fOrder.RemainingAmount)
|
||||
|
||||
ordertype, postonly := getTypeFromTimeInForce(fOrder.TimeInForce)
|
||||
tif, err := timeInForceFromString(fOrder.TimeInForce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &order.Detail{
|
||||
Amount: amount,
|
||||
ExecutedAmount: amount - remaining,
|
||||
@@ -1320,8 +1318,8 @@ func (g *Gateio) GetOrderInfo(ctx context.Context, orderID string, pair currency
|
||||
LastUpdated: fOrder.FinishTime.Time(),
|
||||
Pair: pair,
|
||||
AssetType: a,
|
||||
Type: ordertype,
|
||||
PostOnly: postonly,
|
||||
Type: getTypeFromTimeInForce(fOrder.TimeInForce, fOrder.OrderPrice.Float64()),
|
||||
TimeInForce: tif,
|
||||
Side: side,
|
||||
}, nil
|
||||
case asset.Options:
|
||||
@@ -1507,8 +1505,11 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.MultiOrderReque
|
||||
if futuresOrders[i].Status != statusOpen || (len(req.Pairs) > 0 && !req.Pairs.Contains(pair, true)) {
|
||||
continue
|
||||
}
|
||||
|
||||
side, amount, remaining := getSideAndAmountFromSize(futuresOrders[i].Size, futuresOrders[i].RemainingAmount)
|
||||
tif, err := timeInForceFromString(futuresOrders[i].TimeInForce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orders = append(orders, order.Detail{
|
||||
Status: order.Open,
|
||||
Amount: amount,
|
||||
@@ -1527,7 +1528,7 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.MultiOrderReque
|
||||
Type: order.Limit,
|
||||
SettlementCurrency: settle,
|
||||
ReduceOnly: futuresOrders[i].IsReduceOnly,
|
||||
PostOnly: futuresOrders[i].TimeInForce == "poc",
|
||||
TimeInForce: tif,
|
||||
AverageExecutedPrice: futuresOrders[i].FillPrice.Float64(),
|
||||
})
|
||||
}
|
||||
@@ -2252,16 +2253,17 @@ func getClientOrderIDFromText(text string) string {
|
||||
}
|
||||
|
||||
// getTypeFromTimeInForce returns the order type and if the order is post only
|
||||
func getTypeFromTimeInForce(tif string) (orderType order.Type, postOnly bool) {
|
||||
func getTypeFromTimeInForce(tif string, price float64) (orderType order.Type) {
|
||||
switch tif {
|
||||
case iocTIF:
|
||||
return order.Market, false
|
||||
case fokTIF:
|
||||
return order.Market, false
|
||||
case pocTIF:
|
||||
return order.Limit, true
|
||||
case iocTIF, fokTIF:
|
||||
return order.Market
|
||||
case pocTIF, gtcTIF:
|
||||
return order.Limit
|
||||
default:
|
||||
return order.Limit, false
|
||||
if price == 0 {
|
||||
return order.Market
|
||||
}
|
||||
return order.Limit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2285,27 +2287,6 @@ func getFutureOrderSize(s *order.Submit) (float64, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var errPostOnlyOrderTypeUnsupported = errors.New("post only is only supported for limit orders")
|
||||
|
||||
// getTimeInForce returns the time in force for a given order. If Market order
|
||||
// IOC
|
||||
func getTimeInForce(s *order.Submit) (string, error) {
|
||||
timeInForce := "gtc" // limit order taker/maker
|
||||
if s.Type == order.Market || s.ImmediateOrCancel {
|
||||
timeInForce = iocTIF // market taker only
|
||||
}
|
||||
if s.PostOnly {
|
||||
if s.Type != order.Limit {
|
||||
return "", fmt.Errorf("%w not for %v", errPostOnlyOrderTypeUnsupported, s.Type)
|
||||
}
|
||||
timeInForce = pocTIF // limit order maker only
|
||||
}
|
||||
if s.FillOrKill {
|
||||
timeInForce = fokTIF // limit order entire fill or kill
|
||||
}
|
||||
return timeInForce, nil
|
||||
}
|
||||
|
||||
// GetCurrencyTradeURL returns the URL to the exchange's trade page for the given asset and currency pair
|
||||
func (g *Gateio) GetCurrencyTradeURL(_ context.Context, a asset.Item, cp currency.Pair) (string, error) {
|
||||
_, err := g.CurrencyPairs.IsPairEnabled(cp, a)
|
||||
@@ -2362,17 +2343,12 @@ func (g *Gateio) WebsocketSubmitOrder(ctx context.Context, s *order.Submit) (*or
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeInForce, err := getTimeInForce(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := g.WebsocketFuturesSubmitOrder(ctx, s.AssetType, &ContractOrderCreateParams{
|
||||
Contract: s.Pair,
|
||||
Size: amountWithDirection,
|
||||
Price: strconv.FormatFloat(s.Price, 'f', -1, 64),
|
||||
ReduceOnly: s.ReduceOnly,
|
||||
TimeInForce: timeInForce,
|
||||
TimeInForce: timeInForceString(s.TimeInForce),
|
||||
Text: s.ClientOrderID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -2384,6 +2360,24 @@ func (g *Gateio) WebsocketSubmitOrder(ctx context.Context, s *order.Submit) (*or
|
||||
}
|
||||
}
|
||||
|
||||
// timeInForceString returns the most relevant time-in-force exchange string for a TimeInForce
|
||||
// Any TIF value that is combined with POC, IOC or FOK will just return that
|
||||
// Otherwise the lowercase representation is returned
|
||||
func timeInForceString(tif order.TimeInForce) string {
|
||||
switch {
|
||||
case tif.Is(order.PostOnly):
|
||||
return "poc"
|
||||
case tif.Is(order.ImmediateOrCancel):
|
||||
return iocTIF
|
||||
case tif.Is(order.FillOrKill):
|
||||
return fokTIF
|
||||
case tif.Is(order.GoodTillCancel):
|
||||
return gtcTIF
|
||||
default:
|
||||
return tif.Lower()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Gateio) deriveSpotWebsocketOrderResponse(responses *WebsocketOrderResponse) (*order.SubmitResponse, error) {
|
||||
resp, err := g.deriveSpotWebsocketOrderResponses([]*WebsocketOrderResponse{responses})
|
||||
if err != nil {
|
||||
@@ -2427,7 +2421,10 @@ func (g *Gateio) deriveSpotWebsocketOrderResponses(responses []*WebsocketOrderRe
|
||||
purchased = resp.FilledTotal.Float64()
|
||||
}
|
||||
}
|
||||
|
||||
tif, err := order.StringToTimeInForce(resp.TimeInForce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, &order.SubmitResponse{
|
||||
Exchange: g.Name,
|
||||
OrderID: resp.ID,
|
||||
@@ -2443,9 +2440,7 @@ func (g *Gateio) deriveSpotWebsocketOrderResponses(responses []*WebsocketOrderRe
|
||||
Type: oType,
|
||||
Side: side,
|
||||
Status: status,
|
||||
ImmediateOrCancel: resp.TimeInForce == iocTIF,
|
||||
FillOrKill: resp.TimeInForce == fokTIF,
|
||||
PostOnly: resp.TimeInForce == pocTIF,
|
||||
TimeInForce: tif,
|
||||
Cost: cost,
|
||||
Purchased: purchased,
|
||||
Fee: resp.Fee.Float64(),
|
||||
@@ -2494,7 +2489,10 @@ func (g *Gateio) deriveFuturesWebsocketOrderResponses(responses []*WebsocketFutu
|
||||
if resp.Text != "" && strings.HasPrefix(resp.Text, "t-") {
|
||||
clientOrderID = resp.Text
|
||||
}
|
||||
|
||||
tif, err := order.StringToTimeInForce(resp.TimeInForce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, &order.SubmitResponse{
|
||||
Exchange: g.Name,
|
||||
OrderID: strconv.FormatInt(resp.ID, 10),
|
||||
@@ -2510,9 +2508,7 @@ func (g *Gateio) deriveFuturesWebsocketOrderResponses(responses []*WebsocketFutu
|
||||
Type: oType,
|
||||
Side: side,
|
||||
Status: status,
|
||||
ImmediateOrCancel: resp.TimeInForce == iocTIF,
|
||||
FillOrKill: resp.TimeInForce == fokTIF,
|
||||
PostOnly: resp.TimeInForce == pocTIF,
|
||||
TimeInForce: tif,
|
||||
ReduceOnly: resp.IsReduceOnly,
|
||||
})
|
||||
}
|
||||
@@ -2529,9 +2525,12 @@ func (g *Gateio) getSpotOrderRequest(s *order.Submit) (*CreateOrderRequest, erro
|
||||
return nil, order.ErrSideIsInvalid
|
||||
}
|
||||
|
||||
timeInForce, err := getTimeInForce(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var timeInForce string
|
||||
switch s.TimeInForce {
|
||||
case order.ImmediateOrCancel, order.FillOrKill, order.GoodTillCancel:
|
||||
timeInForce = s.TimeInForce.Lower()
|
||||
case order.PostOnly:
|
||||
timeInForce = "poc"
|
||||
}
|
||||
|
||||
return &CreateOrderRequest{
|
||||
|
||||
Reference in New Issue
Block a user