mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-12 23:36:55 +08:00
@@ -9,19 +9,20 @@ import (
|
|||||||
|
|
||||||
// TraderConfig 单个trader的配置
|
// TraderConfig 单个trader的配置
|
||||||
type TraderConfig struct {
|
type TraderConfig struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
AIModel string `json:"ai_model"` // "qwen" or "deepseek"
|
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"`
|
BinanceAPIKey string `json:"binance_api_key,omitempty"`
|
||||||
BinanceSecretKey string `json:"binance_secret_key,omitempty"`
|
BinanceSecretKey string `json:"binance_secret_key,omitempty"`
|
||||||
|
|
||||||
// Hyperliquid配置
|
// Hyperliquid配置
|
||||||
HyperliquidPrivateKey string `json:"hyperliquid_private_key,omitempty"`
|
HyperliquidPrivateKey string `json:"hyperliquid_private_key,omitempty"`
|
||||||
|
HyperliquidWalletAddr string `json:"hyperliquid_wallet_addr,omitempty"`
|
||||||
HyperliquidTestnet bool `json:"hyperliquid_testnet,omitempty"`
|
HyperliquidTestnet bool `json:"hyperliquid_testnet,omitempty"`
|
||||||
|
|
||||||
// Aster配置
|
// Aster配置
|
||||||
@@ -30,13 +31,13 @@ type TraderConfig struct {
|
|||||||
AsterPrivateKey string `json:"aster_private_key,omitempty"` // Aster API钱包私钥
|
AsterPrivateKey string `json:"aster_private_key,omitempty"` // Aster API钱包私钥
|
||||||
|
|
||||||
// AI配置
|
// AI配置
|
||||||
QwenKey string `json:"qwen_key,omitempty"`
|
QwenKey string `json:"qwen_key,omitempty"`
|
||||||
DeepSeekKey string `json:"deepseek_key,omitempty"`
|
DeepSeekKey string `json:"deepseek_key,omitempty"`
|
||||||
|
|
||||||
// 自定义AI API配置(支持任何OpenAI格式的API)
|
// 自定义AI API配置(支持任何OpenAI格式的API)
|
||||||
CustomAPIURL string `json:"custom_api_url,omitempty"`
|
CustomAPIURL string `json:"custom_api_url,omitempty"`
|
||||||
CustomAPIKey string `json:"custom_api_key,omitempty"`
|
CustomAPIKey string `json:"custom_api_key,omitempty"`
|
||||||
CustomModelName string `json:"custom_model_name,omitempty"`
|
CustomModelName string `json:"custom_model_name,omitempty"`
|
||||||
|
|
||||||
InitialBalance float64 `json:"initial_balance"`
|
InitialBalance float64 `json:"initial_balance"`
|
||||||
ScanIntervalMinutes int `json:"scan_interval_minutes"`
|
ScanIntervalMinutes int `json:"scan_interval_minutes"`
|
||||||
@@ -44,15 +45,15 @@ type TraderConfig struct {
|
|||||||
|
|
||||||
// LeverageConfig 杠杆配置
|
// LeverageConfig 杠杆配置
|
||||||
type LeverageConfig struct {
|
type LeverageConfig struct {
|
||||||
BTCETHLeverage int `json:"btc_eth_leverage"` // BTC和ETH的杠杆倍数(主账户建议5-50,子账户≤5)
|
BTCETHLeverage int `json:"btc_eth_leverage"` // BTC和ETH的杠杆倍数(主账户建议5-50,子账户≤5)
|
||||||
AltcoinLeverage int `json:"altcoin_leverage"` // 山寨币的杠杆倍数(主账户建议5-20,子账户≤5)
|
AltcoinLeverage int `json:"altcoin_leverage"` // 山寨币的杠杆倍数(主账户建议5-20,子账户≤5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config 总配置
|
// Config 总配置
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Traders []TraderConfig `json:"traders"`
|
Traders []TraderConfig `json:"traders"`
|
||||||
UseDefaultCoins bool `json:"use_default_coins"` // 是否使用默认主流币种列表
|
UseDefaultCoins bool `json:"use_default_coins"` // 是否使用默认主流币种列表
|
||||||
DefaultCoins []string `json:"default_coins"` // 默认主流币种池
|
DefaultCoins []string `json:"default_coins"` // 默认主流币种池
|
||||||
CoinPoolAPIURL string `json:"coin_pool_api_url"`
|
CoinPoolAPIURL string `json:"coin_pool_api_url"`
|
||||||
OITopAPIURL string `json:"oi_top_api_url"`
|
OITopAPIURL string `json:"oi_top_api_url"`
|
||||||
APIServerPort int `json:"api_server_port"`
|
APIServerPort int `json:"api_server_port"`
|
||||||
@@ -83,13 +84,13 @@ func LoadConfig(filename string) (*Config, error) {
|
|||||||
if len(config.DefaultCoins) == 0 {
|
if len(config.DefaultCoins) == 0 {
|
||||||
config.DefaultCoins = []string{
|
config.DefaultCoins = []string{
|
||||||
"BTCUSDT",
|
"BTCUSDT",
|
||||||
"ETHUSDT",
|
"ETHUSDT",
|
||||||
"SOLUSDT",
|
"SOLUSDT",
|
||||||
"BNBUSDT",
|
"BNBUSDT",
|
||||||
"XRPUSDT",
|
"XRPUSDT",
|
||||||
"DOGEUSDT",
|
"DOGEUSDT",
|
||||||
"ADAUSDT",
|
"ADAUSDT",
|
||||||
"HYPEUSDT",
|
"HYPEUSDT",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
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.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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ func (tm *TraderManager) AddTrader(cfg config.TraderConfig, coinPoolURL string,
|
|||||||
BinanceAPIKey: cfg.BinanceAPIKey,
|
BinanceAPIKey: cfg.BinanceAPIKey,
|
||||||
BinanceSecretKey: cfg.BinanceSecretKey,
|
BinanceSecretKey: cfg.BinanceSecretKey,
|
||||||
HyperliquidPrivateKey: cfg.HyperliquidPrivateKey,
|
HyperliquidPrivateKey: cfg.HyperliquidPrivateKey,
|
||||||
|
HyperliquidWalletAddr: cfg.HyperliquidWalletAddr,
|
||||||
HyperliquidTestnet: cfg.HyperliquidTestnet,
|
HyperliquidTestnet: cfg.HyperliquidTestnet,
|
||||||
AsterUser: cfg.AsterUser,
|
AsterUser: cfg.AsterUser,
|
||||||
AsterSigner: cfg.AsterSigner,
|
AsterSigner: cfg.AsterSigner,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ type AutoTraderConfig struct {
|
|||||||
|
|
||||||
// Hyperliquid配置
|
// Hyperliquid配置
|
||||||
HyperliquidPrivateKey string
|
HyperliquidPrivateKey string
|
||||||
|
HyperliquidWalletAddr string
|
||||||
HyperliquidTestnet bool
|
HyperliquidTestnet bool
|
||||||
|
|
||||||
// Aster配置
|
// Aster配置
|
||||||
@@ -138,7 +139,7 @@ func NewAutoTrader(config AutoTraderConfig) (*AutoTrader, error) {
|
|||||||
trader = NewFuturesTrader(config.BinanceAPIKey, config.BinanceSecretKey)
|
trader = NewFuturesTrader(config.BinanceAPIKey, config.BinanceSecretKey)
|
||||||
case "hyperliquid":
|
case "hyperliquid":
|
||||||
log.Printf("🏦 [%s] 使用Hyperliquid交易", config.Name)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("初始化Hyperliquid交易器失败: %w", err)
|
return nil, fmt.Errorf("初始化Hyperliquid交易器失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package trader
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -20,7 +19,7 @@ type HyperliquidTrader struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewHyperliquidTrader 创建Hyperliquid交易器
|
// 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)
|
privateKey, err := crypto.HexToECDSA(privateKeyHex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,13 +32,13 @@ func NewHyperliquidTrader(privateKeyHex string, testnet bool) (*HyperliquidTrade
|
|||||||
apiURL = hyperliquid.TestnetAPIURL
|
apiURL = hyperliquid.TestnetAPIURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从私钥生成钱包地址
|
// // 从私钥生成钱包地址
|
||||||
pubKey := privateKey.Public()
|
// pubKey := privateKey.Public()
|
||||||
publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey)
|
// publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey)
|
||||||
if !ok {
|
// if !ok {
|
||||||
return nil, fmt.Errorf("无法转换公钥")
|
// return nil, fmt.Errorf("无法转换公钥")
|
||||||
}
|
// }
|
||||||
walletAddr := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
|
// walletAddr := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
@@ -99,9 +98,9 @@ func (t *HyperliquidTrader) GetBalance() (map[string]interface{}, error) {
|
|||||||
// 钱包余额(已实现)= AccountValue - 未实现盈亏
|
// 钱包余额(已实现)= AccountValue - 未实现盈亏
|
||||||
walletBalance := accountValue - totalUnrealizedPnl
|
walletBalance := accountValue - totalUnrealizedPnl
|
||||||
|
|
||||||
result["totalWalletBalance"] = walletBalance // 钱包余额(已实现部分)
|
result["totalWalletBalance"] = walletBalance // 钱包余额(已实现部分)
|
||||||
result["availableBalance"] = accountValue - totalMarginUsed // 可用余额
|
result["availableBalance"] = accountValue - totalMarginUsed // 可用余额
|
||||||
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未实现盈亏
|
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未实现盈亏
|
||||||
|
|
||||||
log.Printf("✓ Hyperliquid API返回: 账户净值=%.2f, 钱包余额=%.2f, 可用=%.2f, 未实现盈亏=%.2f",
|
log.Printf("✓ Hyperliquid API返回: 账户净值=%.2f, 钱包余额=%.2f, 可用=%.2f, 未实现盈亏=%.2f",
|
||||||
accountValue,
|
accountValue,
|
||||||
@@ -515,8 +514,8 @@ func (t *HyperliquidTrader) SetStopLoss(symbol string, positionSide string, quan
|
|||||||
order := hyperliquid.CreateOrderRequest{
|
order := hyperliquid.CreateOrderRequest{
|
||||||
Coin: coin,
|
Coin: coin,
|
||||||
IsBuy: isBuy,
|
IsBuy: isBuy,
|
||||||
Size: roundedQuantity, // 使用四舍五入后的数量
|
Size: roundedQuantity, // 使用四舍五入后的数量
|
||||||
Price: roundedStopPrice, // 使用处理后的价格
|
Price: roundedStopPrice, // 使用处理后的价格
|
||||||
OrderType: hyperliquid.OrderType{
|
OrderType: hyperliquid.OrderType{
|
||||||
Trigger: &hyperliquid.TriggerOrderType{
|
Trigger: &hyperliquid.TriggerOrderType{
|
||||||
TriggerPx: roundedStopPrice,
|
TriggerPx: roundedStopPrice,
|
||||||
@@ -552,8 +551,8 @@ func (t *HyperliquidTrader) SetTakeProfit(symbol string, positionSide string, qu
|
|||||||
order := hyperliquid.CreateOrderRequest{
|
order := hyperliquid.CreateOrderRequest{
|
||||||
Coin: coin,
|
Coin: coin,
|
||||||
IsBuy: isBuy,
|
IsBuy: isBuy,
|
||||||
Size: roundedQuantity, // 使用四舍五入后的数量
|
Size: roundedQuantity, // 使用四舍五入后的数量
|
||||||
Price: roundedTakeProfitPrice, // 使用处理后的价格
|
Price: roundedTakeProfitPrice, // 使用处理后的价格
|
||||||
OrderType: hyperliquid.OrderType{
|
OrderType: hyperliquid.OrderType{
|
||||||
Trigger: &hyperliquid.TriggerOrderType{
|
Trigger: &hyperliquid.TriggerOrderType{
|
||||||
TriggerPx: roundedTakeProfitPrice,
|
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) {
|
func (t *HyperliquidTrader) FormatQuantity(symbol string, quantity float64) (string, error) {
|
||||||
coin := convertSymbolToHyperliquid(symbol)
|
coin := convertSymbolToHyperliquid(symbol)
|
||||||
szDecimals := t.getSzDecimals(coin)
|
szDecimals := t.getSzDecimals(coin)
|
||||||
|
|
||||||
// 使用szDecimals格式化数量
|
// 使用szDecimals格式化数量
|
||||||
formatStr := fmt.Sprintf("%%.%df", szDecimals)
|
formatStr := fmt.Sprintf("%%.%df", szDecimals)
|
||||||
return fmt.Sprintf(formatStr, quantity), nil
|
return fmt.Sprintf(formatStr, quantity), nil
|
||||||
@@ -604,13 +603,13 @@ func (t *HyperliquidTrader) getSzDecimals(coin string) int {
|
|||||||
// roundToSzDecimals 将数量四舍五入到正确的精度
|
// roundToSzDecimals 将数量四舍五入到正确的精度
|
||||||
func (t *HyperliquidTrader) roundToSzDecimals(coin string, quantity float64) float64 {
|
func (t *HyperliquidTrader) roundToSzDecimals(coin string, quantity float64) float64 {
|
||||||
szDecimals := t.getSzDecimals(coin)
|
szDecimals := t.getSzDecimals(coin)
|
||||||
|
|
||||||
// 计算倍数(10^szDecimals)
|
// 计算倍数(10^szDecimals)
|
||||||
multiplier := 1.0
|
multiplier := 1.0
|
||||||
for i := 0; i < szDecimals; i++ {
|
for i := 0; i < szDecimals; i++ {
|
||||||
multiplier *= 10.0
|
multiplier *= 10.0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 四舍五入
|
// 四舍五入
|
||||||
return float64(int(quantity*multiplier+0.5)) / multiplier
|
return float64(int(quantity*multiplier+0.5)) / multiplier
|
||||||
}
|
}
|
||||||
@@ -621,9 +620,9 @@ func (t *HyperliquidTrader) roundPriceToSigfigs(price float64) float64 {
|
|||||||
if price == 0 {
|
if price == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const sigfigs = 5 // Hyperliquid标准:5位有效数字
|
const sigfigs = 5 // Hyperliquid标准:5位有效数字
|
||||||
|
|
||||||
// 计算价格的数量级
|
// 计算价格的数量级
|
||||||
var magnitude float64
|
var magnitude float64
|
||||||
if price < 0 {
|
if price < 0 {
|
||||||
@@ -631,7 +630,7 @@ func (t *HyperliquidTrader) roundPriceToSigfigs(price float64) float64 {
|
|||||||
} else {
|
} else {
|
||||||
magnitude = price
|
magnitude = price
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算需要的倍数
|
// 计算需要的倍数
|
||||||
multiplier := 1.0
|
multiplier := 1.0
|
||||||
for magnitude >= 10 {
|
for magnitude >= 10 {
|
||||||
@@ -642,12 +641,12 @@ func (t *HyperliquidTrader) roundPriceToSigfigs(price float64) float64 {
|
|||||||
magnitude *= 10
|
magnitude *= 10
|
||||||
multiplier *= 10
|
multiplier *= 10
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用有效数字精度
|
// 应用有效数字精度
|
||||||
for i := 0; i < sigfigs-1; i++ {
|
for i := 0; i < sigfigs-1; i++ {
|
||||||
multiplier *= 10
|
multiplier *= 10
|
||||||
}
|
}
|
||||||
|
|
||||||
// 四舍五入
|
// 四舍五入
|
||||||
rounded := float64(int(price*multiplier+0.5)) / multiplier
|
rounded := float64(int(price*multiplier+0.5)) / multiplier
|
||||||
return rounded
|
return rounded
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import react from '@vitejs/plugin-react'
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
port: 3000,
|
port: 3000,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
|
|||||||
Reference in New Issue
Block a user