perf: reuse shared HTTP client in Hyperliquid trader

Previously each direct API call (orders, sync, account) created a new
http.Client, preventing TCP/TLS connection reuse. Now all calls share
a single client on the HyperliquidTrader struct (30s timeout).
This commit is contained in:
shinchan-zhai
2026-03-23 10:56:54 +08:00
parent 982ee668c9
commit 3c698e3fc5
4 changed files with 11 additions and 8 deletions

View File

@@ -4,10 +4,12 @@ import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"net/http"
"nofx/logger" "nofx/logger"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/sonirico/go-hyperliquid" "github.com/sonirico/go-hyperliquid"
@@ -17,6 +19,7 @@ import (
type HyperliquidTrader struct { type HyperliquidTrader struct {
exchange *hyperliquid.Exchange exchange *hyperliquid.Exchange
ctx context.Context ctx context.Context
httpClient *http.Client // Shared HTTP client for direct API calls (connection reuse)
walletAddr string walletAddr string
meta *hyperliquid.Meta // Cache meta information (including precision) meta *hyperliquid.Meta // Cache meta information (including precision)
metaMutex sync.RWMutex // Protect concurrent access to meta field metaMutex sync.RWMutex // Protect concurrent access to meta field
@@ -220,6 +223,7 @@ func NewHyperliquidTrader(privateKeyHex string, walletAddr string, testnet bool,
return &HyperliquidTrader{ return &HyperliquidTrader{
exchange: exchange, exchange: exchange,
ctx: ctx, ctx: ctx,
httpClient: &http.Client{Timeout: 30 * time.Second},
walletAddr: walletAddr, walletAddr: walletAddr,
meta: meta, meta: meta,
isCrossMargin: true, // Use cross margin mode by default isCrossMargin: true, // Use cross margin mode by default

View File

@@ -217,7 +217,7 @@ func (t *HyperliquidTrader) getXYZDexBalance() (accountValue float64, unrealized
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second} client := t.httpClient
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return 0, 0, nil, fmt.Errorf("failed to execute request: %w", err) return 0, 0, nil, fmt.Errorf("failed to execute request: %w", err)
@@ -308,7 +308,7 @@ func (t *HyperliquidTrader) getXyzMarketPrice(coin string) (float64, error) {
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second} client := t.httpClient
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to execute request: %w", err) return 0, fmt.Errorf("failed to execute request: %w", err)

View File

@@ -445,7 +445,7 @@ func (t *HyperliquidTrader) cancelXyzOrders(coin string) error {
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second} client := t.httpClient
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("failed to execute request: %w", err) return fmt.Errorf("failed to execute request: %w", err)
@@ -538,7 +538,7 @@ func (t *HyperliquidTrader) cancelXyzOrder(oid int64) error {
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second} client := t.httpClient
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("request failed: %w", err) return fmt.Errorf("request failed: %w", err)
@@ -679,7 +679,7 @@ func (t *HyperliquidTrader) placeXyzOrder(coin string, isBuy bool, size float64,
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second} client := t.httpClient
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("request failed: %w", err) return fmt.Errorf("request failed: %w", err)
@@ -843,7 +843,7 @@ func (t *HyperliquidTrader) placeXyzTriggerOrder(coin string, isBuy bool, size f
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second} client := t.httpClient
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("request failed: %w", err) return fmt.Errorf("request failed: %w", err)

View File

@@ -8,7 +8,6 @@ import (
"net/http" "net/http"
"nofx/logger" "nofx/logger"
"strings" "strings"
"time"
) )
// refreshMetaIfNeeded refreshes meta information when invalid (triggered when Asset ID is 0) // refreshMetaIfNeeded refreshes meta information when invalid (triggered when Asset ID is 0)
@@ -67,7 +66,7 @@ func (t *HyperliquidTrader) fetchXyzMeta() error {
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second} client := t.httpClient
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("failed to execute request: %w", err) return fmt.Errorf("failed to execute request: %w", err)