Skip to content

Commit

Permalink
shift tif policies to TimeInForce
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan O'Hara-Reid committed Feb 28, 2025
1 parent 889c7cc commit 4044e90
Show file tree
Hide file tree
Showing 26 changed files with 283 additions and 280 deletions.
2 changes: 0 additions & 2 deletions cmd/exchange_wrapper_issues/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,6 @@ func parseOrderType(orderType string) order.Type {
return order.Limit
case order.Market.String():
return order.Market
case order.ImmediateOrCancel.String():
return order.ImmediateOrCancel
case order.Stop.String():
return order.Stop
case order.TrailingStop.String():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ func generateMethodArg(ctx context.Context, t *testing.T, argGenerator *MethodAr
Amount: 1,
ClientID: "1337",
ClientOrderID: "13371337",
TimeInForce: order.IOC,
TimeInForce: order.ImmediateOrCancel,
Leverage: 1,
})
case argGenerator.MethodInputType.AssignableTo(orderModifyParam):
Expand All @@ -467,7 +467,7 @@ func generateMethodArg(ctx context.Context, t *testing.T, argGenerator *MethodAr
Amount: 1,
ClientOrderID: "13371337",
OrderID: "1337",
TimeInForce: order.IOC,
TimeInForce: order.ImmediateOrCancel,
})
case argGenerator.MethodInputType.AssignableTo(orderCancelParam):
input = reflect.ValueOf(&order.Cancel{
Expand Down
7 changes: 3 additions & 4 deletions engine/order_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,9 @@ func (m *OrderManager) Modify(ctx context.Context, mod *order.Modify) (*order.Mo

// Populate additional Modify fields as some of them are required by various
// exchange implementations.
mod.Pair = det.Pair // Used by Bithumb.
mod.Side = det.Side // Used by Bithumb.
mod.PostOnly = det.PostOnly // Used by Poloniex.
mod.TimeInForce = det.TimeInForce
mod.Pair = det.Pair // Used by Bithumb.
mod.Side = det.Side // Used by Bithumb.
mod.TimeInForce = det.TimeInForce // PostOnly used by Poloniex.

// Following is just a precaution to not modify orders by mistake if exchange
// implementations do not check fields of the Modify struct for zero values.
Expand Down
4 changes: 2 additions & 2 deletions exchanges/binance/binance_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -889,8 +889,8 @@ func (b *Binance) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Subm
timeInForce = ""
requestParamsOrderType = BinanceRequestParamsOrderMarket
case order.Limit:
if s.TimeInForce == order.IOC {
timeInForce = order.IOC.String()
if s.TimeInForce == order.ImmediateOrCancel {
timeInForce = order.ImmediateOrCancel.String()
}
requestParamsOrderType = BinanceRequestParamsOrderLimit
default:
Expand Down
6 changes: 1 addition & 5 deletions exchanges/btcmarkets/btcmarkets.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ const (
askSide = "Ask"
bidSide = "Bid"

// time in force
immediateOrCancel = "IOC"
fillOrKill = "FOK"

subscribe = "subscribe"
fundChange = "fundChange"
orderChange = "orderChange"
Expand Down Expand Up @@ -378,7 +374,7 @@ func (b *BTCMarkets) formatOrderSide(o order.Side) (string, error) {
// getTimeInForce returns a string depending on the options in order.Submit
func (b *BTCMarkets) getTimeInForce(s *order.Submit) string {
switch s.TimeInForce {
case order.IOC, order.FOK:
case order.ImmediateOrCancel, order.FillOrKill:
return s.TimeInForce.String()
default:
return "" // GTC (good till cancelled, default value)
Expand Down
12 changes: 6 additions & 6 deletions exchanges/btcmarkets/btcmarkets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -966,14 +966,14 @@ func TestGetTimeInForce(t *testing.T) {
t.Fatal("unexpected value")
}

f = b.getTimeInForce(&order.Submit{TimeInForce: order.IOC})
if f != immediateOrCancel {
t.Fatalf("received: '%v' but expected: '%v'", f, immediateOrCancel)
f = b.getTimeInForce(&order.Submit{TimeInForce: order.ImmediateOrCancel})
if f != "IOC" {
t.Fatalf("received: '%v' but expected: '%v'", f, "IOC")
}

f = b.getTimeInForce(&order.Submit{TimeInForce: order.FOK})
if f != fillOrKill {
t.Fatalf("received: '%v' but expected: '%v'", f, fillOrKill)
f = b.getTimeInForce(&order.Submit{TimeInForce: order.FillOrKill})
if f != "FOK" {
t.Fatalf("received: '%v' but expected: '%v'", f, "FOK")
}
}

Expand Down
2 changes: 1 addition & 1 deletion exchanges/btcmarkets/btcmarkets_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ func (b *BTCMarkets) GetOrderInfo(ctx context.Context, orderID string, _ currenc
case stop:
resp.Type = order.Stop
case takeProfit:
resp.Type = order.ImmediateOrCancel
resp.Type = order.TakeProfit
default:
resp.Type = order.UnknownType
}
Expand Down
4 changes: 2 additions & 2 deletions exchanges/coinbasepro/coinbasepro_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,8 @@ func (c *CoinbasePro) SubmitOrder(ctx context.Context, s *order.Submit) (*order.
"")
case order.Limit:
timeInForce := order.GoodTillCancel.String()
if s.TimeInForce == order.IOC {
timeInForce = order.IOC.String()
if s.TimeInForce == order.ImmediateOrCancel {
timeInForce = order.ImmediateOrCancel.String()
}
orderID, err = c.PlaceLimitOrder(ctx,
"",
Expand Down
1 change: 0 additions & 1 deletion exchanges/deribit/deribit_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ var (
errWebsocketConnectionNotAuthenticated = errors.New("websocket connection is not authenticated")
errResolutionNotSet = errors.New("resolution not set")
errInvalidDestinationID = errors.New("invalid destination id")
errUnsupportedChannel = errors.New("channels not supported")
errUnacceptableAPIKey = errors.New("unacceptable api key name")
errInvalidUsername = errors.New("new username has to be specified")
errSubAccountNameChangeFailed = errors.New("subaccount name change failed")
Expand Down
2 changes: 0 additions & 2 deletions exchanges/deribit/deribit_websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ var defaultSubscriptions = subscription.List{
}

var (
indexENUMS = []string{"ada_usd", "algo_usd", "avax_usd", "bch_usd", "bnb_usd", "btc_usd", "doge_usd", "dot_usd", "eth_usd", "link_usd", "ltc_usd", "luna_usd", "matic_usd", "near_usd", "shib_usd", "sol_usd", "trx_usd", "uni_usd", "usdc_usd", "xrp_usd", "ada_usdc", "bch_usdc", "algo_usdc", "avax_usdc", "btc_usdc", "doge_usdc", "dot_usdc", "bch_usdc", "bnb_usdc", "eth_usdc", "link_usdc", "ltc_usdc", "luna_usdc", "matic_usdc", "near_usdc", "shib_usdc", "sol_usdc", "trx_usdc", "uni_usdc", "xrp_usdc", "btcdvol_usdc", "ethdvol_usdc"}

pingMessage = WsSubscriptionInput{
ID: 2,
JSONRPCVersion: rpcVersion,
Expand Down
21 changes: 15 additions & 6 deletions exchanges/deribit/deribit_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,9 +597,9 @@ func (d *Deribit) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Subm
timeInForce = "good_til_cancelled"
case order.GoodTillDay:
timeInForce = "good_till_day"
case order.FOK:
case order.FillOrKill:
timeInForce = "fill_or_kill"
case order.IOC:
case order.ImmediateOrCancel:
timeInForce = "immediate_or_cancel"
}
var data *PrivateTradeData
Expand Down Expand Up @@ -794,9 +794,9 @@ func (d *Deribit) GetOrderInfo(ctx context.Context, orderID string, _ currency.P
case "good_til_day":
tif = order.GoodTillDay
case "fill_or_kill":
tif = order.FOK
tif = order.FillOrKill
case "immediate_or_cancel":
tif = order.IOC
tif = order.ImmediateOrCancel
}
return &order.Detail{
AssetType: assetType,
Expand Down Expand Up @@ -919,10 +919,14 @@ func (d *Deribit) GetActiveOrders(ctx context.Context, getOrdersRequest *order.M
if ordersData[y].OrderState != "open" {
continue
}

var tif order.TimeInForce
if ordersData[y].PostOnly { // TODO: Set ordersData[y].TimeInForce values
tif = order.PostOnly
}
resp = append(resp, order.Detail{
AssetType: getOrdersRequest.AssetType,
Exchange: d.Name,
PostOnly: ordersData[y].PostOnly,
Price: ordersData[y].Price,
Amount: ordersData[y].Amount,
ExecutedAmount: ordersData[y].FilledAmount,
Expand All @@ -934,6 +938,7 @@ func (d *Deribit) GetActiveOrders(ctx context.Context, getOrdersRequest *order.M
Side: orderSide,
Type: orderType,
Status: orderStatus,
TimeInForce: tif,
})
}
}
Expand Down Expand Up @@ -988,10 +993,13 @@ func (d *Deribit) GetOrderHistory(ctx context.Context, getOrdersRequest *order.M
return resp, fmt.Errorf("%v: orderStatus %s not supported", d.Name, ordersData[y].OrderState)
}
}
var tif order.TimeInForce
if ordersData[y].PostOnly { // TODO: Set ordersData[y].TimeInForce values
tif = order.PostOnly
}
resp = append(resp, order.Detail{
AssetType: getOrdersRequest.AssetType,
Exchange: d.Name,
PostOnly: ordersData[y].PostOnly,
Price: ordersData[y].Price,
Amount: ordersData[y].Amount,
ExecutedAmount: ordersData[y].FilledAmount,
Expand All @@ -1003,6 +1011,7 @@ func (d *Deribit) GetOrderHistory(ctx context.Context, getOrdersRequest *order.M
Side: orderSide,
Type: orderType,
Status: orderStatus,
TimeInForce: tif,
})
}
}
Expand Down
22 changes: 11 additions & 11 deletions exchanges/gateio/gateio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3370,21 +3370,21 @@ func TestGetClientOrderIDFromText(t *testing.T) {

func TestGetTypeFromTimeInForce(t *testing.T) {
t.Parallel()
typeResp, postOnly := getTypeFromTimeInForce("gtc")
typeResp, tif := getTypeFromTimeInForce("gtc")
assert.Equal(t, order.Limit, typeResp, "should be a limit order")
assert.False(t, postOnly, "should return false")
assert.False(t, tif.Is(order.PostOnly), "should return false")

typeResp, postOnly = getTypeFromTimeInForce("ioc")
typeResp, tif = getTypeFromTimeInForce("ioc")
assert.Equal(t, order.Market, typeResp, "should be market order")
assert.False(t, postOnly, "should return false")
assert.False(t, tif.Is(order.PostOnly), "should return false")

typeResp, postOnly = getTypeFromTimeInForce("poc")
typeResp, tif = getTypeFromTimeInForce("poc")
assert.Equal(t, order.Limit, typeResp, "should be limit order")
assert.True(t, postOnly, "should return true")
assert.True(t, tif.Is(order.PostOnly), "should return true")

typeResp, postOnly = getTypeFromTimeInForce("fok")
typeResp, tif = getTypeFromTimeInForce("fok")
assert.Equal(t, order.Market, typeResp, "should be market order")
assert.False(t, postOnly, "should return false")
assert.False(t, tif.Is(order.PostOnly), "should return false")
}

func TestGetSideAndAmountFromSize(t *testing.T) {
Expand Down Expand Up @@ -3417,22 +3417,22 @@ func TestGetFutureOrderSize(t *testing.T) {
func TestGetTimeInForce(t *testing.T) {
t.Parallel()

_, err := getTimeInForce(&order.Submit{Type: order.Market, TimeInForce: order.PostOnlyGTC})
_, err := getTimeInForce(&order.Submit{Type: order.Market, TimeInForce: order.PostOnly})
assert.ErrorIs(t, err, order.ErrInvalidTimeInForce)

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, TimeInForce: order.PostOnlyGTC})
ret, err = getTimeInForce(&order.Submit{Type: order.Limit, TimeInForce: order.PostOnly})
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, TimeInForce: order.FOK})
ret, err = getTimeInForce(&order.Submit{Type: order.Market, TimeInForce: order.FillOrKill})
require.NoError(t, err)
assert.Equal(t, "fok", ret)
}
Expand Down
29 changes: 18 additions & 11 deletions exchanges/gateio/gateio_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,7 @@ func (g *Gateio) GetOrderInfo(ctx context.Context, orderID string, pair currency

side, amount, remaining := getSideAndAmountFromSize(fOrder.Size, fOrder.RemainingAmount)

ordertype, postonly := getTypeFromTimeInForce(fOrder.TimeInForce)
ordertype, tif := getTypeFromTimeInForce(fOrder.TimeInForce)
return &order.Detail{
Amount: amount,
ExecutedAmount: amount - remaining,
Expand All @@ -1504,7 +1504,7 @@ func (g *Gateio) GetOrderInfo(ctx context.Context, orderID string, pair currency
Pair: pair,
AssetType: a,
Type: ordertype,
PostOnly: postonly,
TimeInForce: tif,
Side: side,
}, nil
case asset.Options:
Expand Down Expand Up @@ -1719,6 +1719,11 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.MultiOrderReque
continue
}

var tif order.TimeInForce
if futuresOrders[x].TimeInForce == "poc" {
tif = order.PostOnly
}

side, amount, remaining := getSideAndAmountFromSize(futuresOrders[x].Size, futuresOrders[x].RemainingAmount)
orders = append(orders, order.Detail{
Status: order.Open,
Expand All @@ -1738,7 +1743,7 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.MultiOrderReque
Type: order.Limit,
SettlementCurrency: settlement,
ReduceOnly: futuresOrders[x].IsReduceOnly,
PostOnly: futuresOrders[x].TimeInForce == "poc",
TimeInForce: tif,
AverageExecutedPrice: futuresOrders[x].FillPrice.Float64(),
})
}
Expand Down Expand Up @@ -2506,16 +2511,18 @@ 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) {
// TODO: Add in price param to correctly determine if order is market or limit as a zero value price must be a market order
// IOC and POC can be limits if price is supplied.
func getTypeFromTimeInForce(tif string) (orderType order.Type, postOnly order.TimeInForce) {
switch tif {
case "ioc":
return order.Market, false
return order.Market, order.UnsetTIF
case "fok":
return order.Market, false
return order.Market, order.UnsetTIF
case "poc":
return order.Limit, true
return order.Limit, order.PostOnly
default:
return order.Limit, false
return order.Limit, order.UnsetTIF
}
}

Expand Down Expand Up @@ -2544,11 +2551,11 @@ func getFutureOrderSize(s *order.Submit) (float64, error) {
func getTimeInForce(s *order.Submit) (string, error) {
var timeInForce string
switch s.TimeInForce {
case order.IOC:
case order.ImmediateOrCancel:
timeInForce = "ioc" // market taker only
case order.FOK:
case order.FillOrKill:
timeInForce = "fok"
case order.PostOnlyGTC:
case order.PostOnly:
timeInForce = "poc"
case order.GoodTillCancel:
timeInForce = "gtc"
Expand Down
9 changes: 5 additions & 4 deletions exchanges/huobi/huobi_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -980,10 +980,11 @@ type WsTradeUpdate struct {

// OrderVars stores side, status and type for any order/trade
type OrderVars struct {
Side order.Side
Status order.Status
OrderType order.Type
Fee float64
Side order.Side
Status order.Status
OrderType order.Type
TimeInForce order.TimeInForce
Fee float64
}

// Variables below are used to check api requests being sent out
Expand Down
Loading

0 comments on commit 4044e90

Please sign in to comment.