Merge pull request #59 from liugangdao/main

fix: hyperliquid余额不准确
This commit is contained in:
tinkle-community
2025-10-30 17:05:54 +08:00
committed by GitHub
6 changed files with 52 additions and 47 deletions

View File

@@ -9,19 +9,20 @@ import (
// TraderConfig 单个trader的配置
type TraderConfig struct {
ID string `json:"id"`
Name string `json:"name"`
AIModel string `json:"ai_model"` // "qwen" or "deepseek"
ID string `json:"id"`
Name string `json:"name"`
AIModel string `json:"ai_model"` // "qwen" or "deepseek"
// 交易平台选择(二选一)
Exchange string `json:"exchange"` // "binance" or "hyperliquid"
Exchange string `json:"exchange"` // "binance" or "hyperliquid"
// 币安配置
BinanceAPIKey string `json:"binance_api_key,omitempty"`
BinanceSecretKey string `json:"binance_secret_key,omitempty"`
BinanceAPIKey string `json:"binance_api_key,omitempty"`
BinanceSecretKey string `json:"binance_secret_key,omitempty"`
// Hyperliquid配置
HyperliquidPrivateKey string `json:"hyperliquid_private_key,omitempty"`
HyperliquidWalletAddr string `json:"hyperliquid_wallet_addr,omitempty"`
HyperliquidTestnet bool `json:"hyperliquid_testnet,omitempty"`
// Aster配置
@@ -30,13 +31,13 @@ type TraderConfig struct {
AsterPrivateKey string `json:"aster_private_key,omitempty"` // Aster API钱包私钥
// AI配置
QwenKey string `json:"qwen_key,omitempty"`
DeepSeekKey string `json:"deepseek_key,omitempty"`
QwenKey string `json:"qwen_key,omitempty"`
DeepSeekKey string `json:"deepseek_key,omitempty"`
// 自定义AI API配置支持任何OpenAI格式的API
CustomAPIURL string `json:"custom_api_url,omitempty"`
CustomAPIKey string `json:"custom_api_key,omitempty"`
CustomModelName string `json:"custom_model_name,omitempty"`
CustomAPIURL string `json:"custom_api_url,omitempty"`
CustomAPIKey string `json:"custom_api_key,omitempty"`
CustomModelName string `json:"custom_model_name,omitempty"`
InitialBalance float64 `json:"initial_balance"`
ScanIntervalMinutes int `json:"scan_interval_minutes"`
@@ -44,15 +45,15 @@ type TraderConfig struct {
// LeverageConfig 杠杆配置
type LeverageConfig struct {
BTCETHLeverage int `json:"btc_eth_leverage"` // BTC和ETH的杠杆倍数主账户建议5-50子账户≤5
AltcoinLeverage int `json:"altcoin_leverage"` // 山寨币的杠杆倍数主账户建议5-20子账户≤5
BTCETHLeverage int `json:"btc_eth_leverage"` // BTC和ETH的杠杆倍数主账户建议5-50子账户≤5
AltcoinLeverage int `json:"altcoin_leverage"` // 山寨币的杠杆倍数主账户建议5-20子账户≤5
}
// Config 总配置
type Config struct {
Traders []TraderConfig `json:"traders"`
UseDefaultCoins bool `json:"use_default_coins"` // 是否使用默认主流币种列表
DefaultCoins []string `json:"default_coins"` // 默认主流币种池
UseDefaultCoins bool `json:"use_default_coins"` // 是否使用默认主流币种列表
DefaultCoins []string `json:"default_coins"` // 默认主流币种池
CoinPoolAPIURL string `json:"coin_pool_api_url"`
OITopAPIURL string `json:"oi_top_api_url"`
APIServerPort int `json:"api_server_port"`
@@ -83,13 +84,13 @@ func LoadConfig(filename string) (*Config, error) {
if len(config.DefaultCoins) == 0 {
config.DefaultCoins = []string{
"BTCUSDT",
"ETHUSDT",
"SOLUSDT",
"BNBUSDT",
"XRPUSDT",
"DOGEUSDT",
"ADAUSDT",
"HYPEUSDT",
"ETHUSDT",
"SOLUSDT",
"BNBUSDT",
"XRPUSDT",
"DOGEUSDT",
"ADAUSDT",
"HYPEUSDT",
}
}

2
go.sum
View File

@@ -74,6 +74,8 @@ github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=

View File

@@ -40,6 +40,7 @@ func (tm *TraderManager) AddTrader(cfg config.TraderConfig, coinPoolURL string,
BinanceAPIKey: cfg.BinanceAPIKey,
BinanceSecretKey: cfg.BinanceSecretKey,
HyperliquidPrivateKey: cfg.HyperliquidPrivateKey,
HyperliquidWalletAddr: cfg.HyperliquidWalletAddr,
HyperliquidTestnet: cfg.HyperliquidTestnet,
AsterUser: cfg.AsterUser,
AsterSigner: cfg.AsterSigner,

View File

@@ -29,6 +29,7 @@ type AutoTraderConfig struct {
// Hyperliquid配置
HyperliquidPrivateKey string
HyperliquidWalletAddr string
HyperliquidTestnet bool
// Aster配置
@@ -138,7 +139,7 @@ func NewAutoTrader(config AutoTraderConfig) (*AutoTrader, error) {
trader = NewFuturesTrader(config.BinanceAPIKey, config.BinanceSecretKey)
case "hyperliquid":
log.Printf("🏦 [%s] 使用Hyperliquid交易", config.Name)
trader, err = NewHyperliquidTrader(config.HyperliquidPrivateKey, config.HyperliquidTestnet)
trader, err = NewHyperliquidTrader(config.HyperliquidPrivateKey, config.HyperliquidWalletAddr, config.HyperliquidTestnet)
if err != nil {
return nil, fmt.Errorf("初始化Hyperliquid交易器失败: %w", err)
}

View File

@@ -2,7 +2,6 @@ package trader
import (
"context"
"crypto/ecdsa"
"fmt"
"log"
"strconv"
@@ -20,7 +19,7 @@ type HyperliquidTrader struct {
}
// NewHyperliquidTrader 创建Hyperliquid交易器
func NewHyperliquidTrader(privateKeyHex string, testnet bool) (*HyperliquidTrader, error) {
func NewHyperliquidTrader(privateKeyHex string, walletAddr string, testnet bool) (*HyperliquidTrader, error) {
// 解析私钥
privateKey, err := crypto.HexToECDSA(privateKeyHex)
if err != nil {
@@ -33,13 +32,13 @@ func NewHyperliquidTrader(privateKeyHex string, testnet bool) (*HyperliquidTrade
apiURL = hyperliquid.TestnetAPIURL
}
// 从私钥生成钱包地址
pubKey := privateKey.Public()
publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("无法转换公钥")
}
walletAddr := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
// // 从私钥生成钱包地址
// pubKey := privateKey.Public()
// publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey)
// if !ok {
// return nil, fmt.Errorf("无法转换公钥")
// }
// walletAddr := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
ctx := context.Background()
@@ -99,9 +98,9 @@ func (t *HyperliquidTrader) GetBalance() (map[string]interface{}, error) {
// 钱包余额(已实现)= AccountValue - 未实现盈亏
walletBalance := accountValue - totalUnrealizedPnl
result["totalWalletBalance"] = walletBalance // 钱包余额(已实现部分)
result["availableBalance"] = accountValue - totalMarginUsed // 可用余额
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未实现盈亏
result["totalWalletBalance"] = walletBalance // 钱包余额(已实现部分)
result["availableBalance"] = accountValue - totalMarginUsed // 可用余额
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未实现盈亏
log.Printf("✓ Hyperliquid API返回: 账户净值=%.2f, 钱包余额=%.2f, 可用=%.2f, 未实现盈亏=%.2f",
accountValue,
@@ -515,8 +514,8 @@ func (t *HyperliquidTrader) SetStopLoss(symbol string, positionSide string, quan
order := hyperliquid.CreateOrderRequest{
Coin: coin,
IsBuy: isBuy,
Size: roundedQuantity, // 使用四舍五入后的数量
Price: roundedStopPrice, // 使用处理后的价格
Size: roundedQuantity, // 使用四舍五入后的数量
Price: roundedStopPrice, // 使用处理后的价格
OrderType: hyperliquid.OrderType{
Trigger: &hyperliquid.TriggerOrderType{
TriggerPx: roundedStopPrice,
@@ -552,8 +551,8 @@ func (t *HyperliquidTrader) SetTakeProfit(symbol string, positionSide string, qu
order := hyperliquid.CreateOrderRequest{
Coin: coin,
IsBuy: isBuy,
Size: roundedQuantity, // 使用四舍五入后的数量
Price: roundedTakeProfitPrice, // 使用处理后的价格
Size: roundedQuantity, // 使用四舍五入后的数量
Price: roundedTakeProfitPrice, // 使用处理后的价格
OrderType: hyperliquid.OrderType{
Trigger: &hyperliquid.TriggerOrderType{
TriggerPx: roundedTakeProfitPrice,
@@ -577,7 +576,7 @@ func (t *HyperliquidTrader) SetTakeProfit(symbol string, positionSide string, qu
func (t *HyperliquidTrader) FormatQuantity(symbol string, quantity float64) (string, error) {
coin := convertSymbolToHyperliquid(symbol)
szDecimals := t.getSzDecimals(coin)
// 使用szDecimals格式化数量
formatStr := fmt.Sprintf("%%.%df", szDecimals)
return fmt.Sprintf(formatStr, quantity), nil
@@ -604,13 +603,13 @@ func (t *HyperliquidTrader) getSzDecimals(coin string) int {
// roundToSzDecimals 将数量四舍五入到正确的精度
func (t *HyperliquidTrader) roundToSzDecimals(coin string, quantity float64) float64 {
szDecimals := t.getSzDecimals(coin)
// 计算倍数10^szDecimals
multiplier := 1.0
for i := 0; i < szDecimals; i++ {
multiplier *= 10.0
}
// 四舍五入
return float64(int(quantity*multiplier+0.5)) / multiplier
}
@@ -621,9 +620,9 @@ func (t *HyperliquidTrader) roundPriceToSigfigs(price float64) float64 {
if price == 0 {
return 0
}
const sigfigs = 5 // Hyperliquid标准5位有效数字
// 计算价格的数量级
var magnitude float64
if price < 0 {
@@ -631,7 +630,7 @@ func (t *HyperliquidTrader) roundPriceToSigfigs(price float64) float64 {
} else {
magnitude = price
}
// 计算需要的倍数
multiplier := 1.0
for magnitude >= 10 {
@@ -642,12 +641,12 @@ func (t *HyperliquidTrader) roundPriceToSigfigs(price float64) float64 {
magnitude *= 10
multiplier *= 10
}
// 应用有效数字精度
for i := 0; i < sigfigs-1; i++ {
multiplier *= 10
}
// 四舍五入
rounded := float64(int(price*multiplier+0.5)) / multiplier
return rounded

View File

@@ -4,6 +4,7 @@ import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
host: '0.0.0.0',
port: 3000,
proxy: {
'/api': {