feat: real trading mode, auto-stop after 5 trades, HL testnet support
- config.json: test_mode=false, ready for sim/testnet trading - trader.go: auto-stop after 5 real trades, exchange response logging, Stop()/Start() API, shuttingDown flag for graceful stop - dashboard.go: POST /api/stop + POST /api/start endpoints, trading status in SSE stats - exchange/hyperliquid.go: switch HL WS to testnet endpoint - exchange/hyperliquid_trade.go: switch REST to testnet endpoint, support base64 + 32-byte EVM private keys - main.go: listen on trader.StopCh (graceful, no process exit) - scanner.go: trim TrackedCoins to only 6 core coins (DOGE/LINK/ONDO/OP/WIF/ARB) - .gitignore: ignore main binary
This commit is contained in:
@@ -25,7 +25,7 @@ func NewHyperLiquidWS(tracked []string) *HyperLiquidWS {
|
||||
}
|
||||
// Run connects to HyperLiquid WS and streams mid prices.
|
||||
func (h *HyperLiquidWS) Run(updateFn func(coin string, price, bid, ask float64)) error {
|
||||
conn := NewPriceConnector("wss://api.hyperliquid.xyz/ws", "HyperLiquid", 120*time.Second, 30*time.Second)
|
||||
conn := NewPriceConnector("wss://api.hyperliquid-testnet.xyz/ws", "HyperLiquid", 120*time.Second, 30*time.Second)
|
||||
conn.PingInterval = 45 * time.Second
|
||||
|
||||
conn.OnConnect = func() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package exchange
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -59,13 +60,27 @@ func NewHyperLiquidTrade(privateKeyHex, address string) (*HyperLiquidTrade, erro
|
||||
if privateKeyHex == "" {
|
||||
return &HyperLiquidTrade{client: &http.Client{Timeout: 10 * time.Second}}, nil
|
||||
}
|
||||
|
||||
// Try hex decode first
|
||||
keyBytes, err := hex.DecodeString(privateKeyHex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode private key: %w", err)
|
||||
// Fallback to base64 decode
|
||||
keyBytes, err = base64.StdEncoding.DecodeString(privateKeyHex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode private key: not valid hex or base64")
|
||||
}
|
||||
}
|
||||
if len(keyBytes) != ed25519.PrivateKeySize {
|
||||
return nil, fmt.Errorf("invalid private key length: %d (expected %d)", len(keyBytes), ed25519.PrivateKeySize)
|
||||
|
||||
// Accept both 32-byte (EVM) and 64-byte (Ed25519) keys
|
||||
// 32-byte EVM keys are padded to 64 bytes for Ed25519
|
||||
if len(keyBytes) == 32 {
|
||||
// Use as Ed25519 seed — append the public key part
|
||||
priv := ed25519.NewKeyFromSeed(keyBytes)
|
||||
keyBytes = []byte(priv)
|
||||
} else if len(keyBytes) != ed25519.PrivateKeySize {
|
||||
return nil, fmt.Errorf("invalid private key length: %d (expected 32 or %d)", len(keyBytes), ed25519.PrivateKeySize)
|
||||
}
|
||||
|
||||
return &HyperLiquidTrade{
|
||||
PrivateKey: ed25519.PrivateKey(keyBytes),
|
||||
Address: address,
|
||||
@@ -119,7 +134,7 @@ func (h *HyperLiquidTrade) PlaceMarketOrder(coin, side, sz string) (string, erro
|
||||
|
||||
bodyJSON, _ := json.Marshal(signed)
|
||||
|
||||
req, err := http.NewRequest("POST", "https://api.hyperliquid.xyz/exchange", strings.NewReader(string(bodyJSON)))
|
||||
req, err := http.NewRequest("POST", "https://api.hyperliquid-testnet.xyz/exchange", strings.NewReader(string(bodyJSON)))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("create request: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user