Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hyperliquid/consts.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package hyperliquid

const GLOBAL_DEBUG = false // Defualt debug that is used in all tests
const GLOBAL_DEBUG = false // Default debug that is used in all tests

// Execution constants
const DEFAULT_SLIPPAGE = 0.005 // 0.5% default slippage
Expand Down
1 change: 1 addition & 0 deletions hyperliquid/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func OrderRequestToWire(req OrderRequest, meta map[string]AssetInfo, isSpot bool
SizePx: FloatToWire(req.Sz, maxDecimals, info.SzDecimals),
ReduceOnly: req.ReduceOnly,
OrderType: OrderTypeToWire(req.OrderType),
Cloid: req.Cloid,
}
}

Expand Down
42 changes: 38 additions & 4 deletions hyperliquid/exchange_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ type IExchangeAPI interface {
// Open orders
BulkOrders(requests []OrderRequest, grouping Grouping) (*PlaceOrderResponse, error)
Order(request OrderRequest, grouping Grouping) (*PlaceOrderResponse, error)
MarketOrder(coin string, size float64, slippage *float64) (*PlaceOrderResponse, error)
LimitOrder(orderType string, coin string, size float64, px float64, isBuy bool, reduceOnly bool) (*PlaceOrderResponse, error)
MarketOrder(coin string, size float64, slippage *float64, clientOID ...string) (*PlaceOrderResponse, error)
LimitOrder(orderType string, coin string, size float64, px float64, isBuy bool, reduceOnly bool, clientOID ...string) (*PlaceOrderResponse, error)

// Order management
CancelOrderByOID(coin string, orderID int) (any, error)
CancelOrderByCloid(coin string, clientOID string) (any, error)
BulkCancelOrders(cancels []CancelOidWire) (any, error)
CancelAllOrdersByCoin(coin string) (any, error)
CancelAllOrders() (any, error)
Expand Down Expand Up @@ -98,7 +99,7 @@ func (api *ExchangeAPI) SlippagePriceSpot(coin string, isBuy bool, slippage floa
// MarketOrder("BTC", 0.1, nil) // Buy 0.1 BTC
// MarketOrder("BTC", -0.1, nil) // Sell 0.1 BTC
// MarketOrder("BTC", 0.1, &slippage) // Buy 0.1 BTC with slippage
func (api *ExchangeAPI) MarketOrder(coin string, size float64, slippage *float64) (*PlaceOrderResponse, error) {
func (api *ExchangeAPI) MarketOrder(coin string, size float64, slippage *float64, clientOID ...string) (*PlaceOrderResponse, error) {
slpg := GetSlippage(slippage)
isBuy := IsBuy(size)
finalPx := api.SlippagePrice(coin, isBuy, slpg)
Expand All @@ -115,6 +116,9 @@ func (api *ExchangeAPI) MarketOrder(coin string, size float64, slippage *float64
OrderType: orderType,
ReduceOnly: false,
}
if len(clientOID) > 0 {
orderRequest.Cloid = clientOID[0]
}
return api.Order(orderRequest, GroupingNa)
}

Expand Down Expand Up @@ -150,7 +154,7 @@ func (api *ExchangeAPI) MarketOrderSpot(coin string, size float64, slippage *flo
// Order type can be Gtc, Ioc, Alo.
// Size determines the amount of the coin to buy/sell.
// See the constants TifGtc, TifIoc, TifAlo.
func (api *ExchangeAPI) LimitOrder(orderType string, coin string, size float64, px float64, reduceOnly bool) (*PlaceOrderResponse, error) {
func (api *ExchangeAPI) LimitOrder(orderType string, coin string, size float64, px float64, reduceOnly bool, clientOID ...string) (*PlaceOrderResponse, error) {
// check if the order type is valid
if orderType != TifGtc && orderType != TifIoc && orderType != TifAlo {
return nil, APIError{Message: fmt.Sprintf("Invalid order type: %s. Available types: %s, %s, %s", orderType, TifGtc, TifIoc, TifAlo)}
Expand All @@ -168,6 +172,9 @@ func (api *ExchangeAPI) LimitOrder(orderType string, coin string, size float64,
OrderType: orderTypeZ,
ReduceOnly: reduceOnly,
}
if len(clientOID) > 0 {
orderRequest.Cloid = clientOID[0]
}
return api.Order(orderRequest, GroupingNa)
}

Expand Down Expand Up @@ -304,6 +311,33 @@ func (api *ExchangeAPI) CancelOrderByOID(coin string, orderID int64) (*CancelOrd
return api.BulkCancelOrders([]CancelOidWire{{Asset: api.meta[coin].AssetId, Oid: int(orderID)}})
}

// Cancel exact order by Client Order Id
// https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#cancel-order-s-by-cloid
func (api *ExchangeAPI) CancelOrderByCloid(coin string, clientOID string) (*CancelOrderResponse, error) {
timestamp := GetNonce()
action := CancelCloidOrderAction{
Type: "cancelByCloid",
Cancels: []CancelCloidWire{
{
Asset: api.meta[coin].AssetId,
Cloid: clientOID,
},
},
}
v, r, s, err := api.SignL1Action(action, timestamp)
if err != nil {
api.debug("Error signing L1 action: %s", err)
return nil, err
}
request := ExchangeRequest{
Action: action,
Nonce: timestamp,
Signature: ToTypedSig(r, s, v),
VaultAddress: nil,
}
return MakeUniversalRequest[CancelOrderResponse](api, request)
}

// Cancel all orders for a given coin
func (api *ExchangeAPI) CancelAllOrdersByCoin(coin string) (*CancelOrderResponse, error) {
orders, err := api.infoAPI.GetOpenOrders(api.AccountAddress())
Expand Down
15 changes: 14 additions & 1 deletion hyperliquid/exchange_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type OrderRequest struct {
LimitPx float64 `json:"limit_px"`
OrderType OrderType `json:"order_type"`
ReduceOnly bool `json:"reduce_only"`
Cloid string `json:"cloid,omitempty"`
}

type OrderType struct {
Expand Down Expand Up @@ -145,6 +146,16 @@ type CancelOidWire struct {
Oid int `msgpack:"o" json:"o"`
}

type CancelCloidWire struct {
Asset int `msgpack:"asset" json:"asset"`
Cloid string `msgpack:"cloid" json:"cloid"`
}

type CancelCloidOrderAction struct {
Type string `msgpack:"type" json:"type"`
Cancels []CancelCloidWire `msgpack:"cancels" json:"cancels"`
}

type CancelOrderResponse struct {
Status string `json:"status"`
Response InnerCancelResponse `json:"response"`
Expand All @@ -160,7 +171,8 @@ type CancelResponseStatuses struct {
}

type RestingStatus struct {
OrderId int `json:"oid"`
OrderId int `json:"oid"`
Cloid string `json:"cloid,omitempty"`
}

type CloseRequest struct {
Expand All @@ -175,6 +187,7 @@ type FilledStatus struct {
OrderId int `json:"oid"`
AvgPx float64 `json:"avgPx,string"`
TotalSz float64 `json:"totalSz,string"`
Cloid string `json:"cloid,omitempty"`
}

type Liquidation struct {
Expand Down
28 changes: 18 additions & 10 deletions hyperliquid/hyperliquid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,38 +70,46 @@ func TestHyperliquid_MakeSomeTradingLogic(t *testing.T) {
}
t.Logf("LimitOrder(TifGtc, ETH, -0.01, 5000.1, true): %v", res3)

res4, err := client.LimitOrder(TifGtc, "ETH", 0.01, 1234.1, false, "0x1234567890abcdef1234567890abcdef")
if err != nil {
if err != nil {
t.Errorf("Error: %v", err)
}
}
t.Logf("LimitOrder(TifIoc, ETH, 0.01, 1234.1, false, 0x1234567890abcdef1234567890abcdef): %v", res4)

// Get all ordres
res4, err := client.GetAccountOpenOrders()
res5, err := client.GetAccountOpenOrders()
if err != nil {
t.Errorf("Error: %v", err)
}
t.Logf("GetAccountOpenOrders(): %v", res4)
t.Logf("GetAccountOpenOrders(): %v", res5)

// Close all orders
res5, err := client.CancelAllOrders()
res6, err := client.CancelAllOrders()
if err != nil {
t.Errorf("Error: %v", err)
}
t.Logf("CancelAllOrders(): %v", res5)
t.Logf("CancelAllOrders(): %v", res6)

// Make market order
res6, err := client.MarketOrder("ETH", 0.01, nil)
res7, err := client.MarketOrder("ETH", 0.01, nil)
if err != nil {
t.Errorf("Error: %v", err)
}
t.Logf("MarketOrder(ETH, 0.01, nil): %v", res6)
t.Logf("MarketOrder(ETH, 0.01, nil): %v", res7)

// Close position
res7, err := client.ClosePosition("ETH")
res8, err := client.ClosePosition("ETH")
if err != nil {
t.Errorf("Error: %v", err)
}
t.Logf("ClosePosition(ETH): %v", res7)
t.Logf("ClosePosition(ETH): %v", res8)

// Get account balance
res8, err := client.GetAccountState()
res9, err := client.GetAccountState()
if err != nil {
t.Errorf("Error: %v", err)
}
t.Logf("GetAccountState(): %v", res8)
t.Logf("GetAccountState(): %v", res9)
}