mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
fix(bootstrap module): add bootstrap module to meet future function (#674)
* fix(bootstrap module): add bootstrap module to meet future function * Fix readme * Fix panic because log.logger is nil * fix import --------- Co-authored-by: zbhan <zbhan@freewheel.tv>
This commit is contained in:
193
config/config.go
193
config/config.go
@@ -3,47 +3,10 @@ package config
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TraderConfig 单个trader的配置
|
||||
type TraderConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"` // 是否启用该trader
|
||||
AIModel string `json:"ai_model"` // "qwen" or "deepseek"
|
||||
|
||||
// 交易平台选择(二选一)
|
||||
Exchange string `json:"exchange"` // "binance" or "hyperliquid"
|
||||
|
||||
// 币安配置
|
||||
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配置
|
||||
AsterUser string `json:"aster_user,omitempty"` // Aster主钱包地址
|
||||
AsterSigner string `json:"aster_signer,omitempty"` // Aster API钱包地址
|
||||
AsterPrivateKey string `json:"aster_private_key,omitempty"` // Aster API钱包私钥
|
||||
|
||||
// AI配置
|
||||
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"`
|
||||
|
||||
InitialBalance float64 `json:"initial_balance"`
|
||||
ScanIntervalMinutes int `json:"scan_interval_minutes"`
|
||||
}
|
||||
|
||||
// LeverageConfig 杠杆配置
|
||||
type LeverageConfig struct {
|
||||
BTCETHLeverage int `json:"btc_eth_leverage"` // BTC和ETH的杠杆倍数(主账户建议5-50,子账户≤5)
|
||||
@@ -66,149 +29,41 @@ type TelegramConfig struct {
|
||||
|
||||
// Config 总配置
|
||||
type Config struct {
|
||||
Traders []TraderConfig `json:"traders"`
|
||||
UseDefaultCoins bool `json:"use_default_coins"` // 是否使用默认主流币种列表
|
||||
DefaultCoins []string `json:"default_coins"` // 默认主流币种池
|
||||
AdminMode bool `json:"admin_mode"`
|
||||
BetaMode bool `json:"beta_mode"`
|
||||
APIServerPort int `json:"api_server_port"`
|
||||
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"`
|
||||
MaxDailyLoss float64 `json:"max_daily_loss"`
|
||||
MaxDrawdown float64 `json:"max_drawdown"`
|
||||
StopTradingMinutes int `json:"stop_trading_minutes"`
|
||||
Leverage LeverageConfig `json:"leverage"` // 杠杆配置
|
||||
Log *LogConfig `json:"log"` // 日志配置(可选)
|
||||
Leverage LeverageConfig `json:"leverage"`
|
||||
JWTSecret string `json:"jwt_secret"`
|
||||
DataKLineTime string `json:"data_k_line_time"`
|
||||
Log *LogConfig `json:"log"` // 日志配置
|
||||
}
|
||||
|
||||
// LoadConfig 从文件加载配置
|
||||
func LoadConfig(filename string) (*Config, error) {
|
||||
// 检查filename是否存在
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
log.Printf("📄 %s不存在,使用默认配置", filename)
|
||||
return &Config{}, nil
|
||||
}
|
||||
|
||||
// 读取 filename
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取配置文件失败: %w", err)
|
||||
return nil, fmt.Errorf("读取%s失败: %w", filename, err)
|
||||
}
|
||||
|
||||
var config Config
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("解析配置文件失败: %w", err)
|
||||
// 解析JSON
|
||||
var configFile Config
|
||||
if err := json.Unmarshal(data, &configFile); err != nil {
|
||||
return nil, fmt.Errorf("解析%s失败: %w", filename, err)
|
||||
}
|
||||
|
||||
// 设置默认值:确保使用默认币种列表
|
||||
if !config.UseDefaultCoins {
|
||||
config.UseDefaultCoins = true
|
||||
}
|
||||
|
||||
// 设置默认币种池
|
||||
if len(config.DefaultCoins) == 0 {
|
||||
config.DefaultCoins = []string{
|
||||
"BTCUSDT",
|
||||
"ETHUSDT",
|
||||
"SOLUSDT",
|
||||
"BNBUSDT",
|
||||
"XRPUSDT",
|
||||
"DOGEUSDT",
|
||||
"ADAUSDT",
|
||||
"HYPEUSDT",
|
||||
}
|
||||
}
|
||||
|
||||
// 验证配置
|
||||
if err := config.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("配置验证失败: %w", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// Validate 验证配置有效性
|
||||
func (c *Config) Validate() error {
|
||||
if len(c.Traders) == 0 {
|
||||
return fmt.Errorf("至少需要配置一个trader")
|
||||
}
|
||||
|
||||
traderIDs := make(map[string]bool)
|
||||
for i, trader := range c.Traders {
|
||||
if trader.ID == "" {
|
||||
return fmt.Errorf("trader[%d]: ID不能为空", i)
|
||||
}
|
||||
if traderIDs[trader.ID] {
|
||||
return fmt.Errorf("trader[%d]: ID '%s' 重复", i, trader.ID)
|
||||
}
|
||||
traderIDs[trader.ID] = true
|
||||
|
||||
if trader.Name == "" {
|
||||
return fmt.Errorf("trader[%d]: Name不能为空", i)
|
||||
}
|
||||
if trader.AIModel != "qwen" && trader.AIModel != "deepseek" && trader.AIModel != "custom" {
|
||||
return fmt.Errorf("trader[%d]: ai_model必须是 'qwen', 'deepseek' 或 'custom'", i)
|
||||
}
|
||||
|
||||
// 验证交易平台配置
|
||||
if trader.Exchange == "" {
|
||||
trader.Exchange = "binance" // 默认使用币安
|
||||
}
|
||||
if trader.Exchange != "binance" && trader.Exchange != "hyperliquid" && trader.Exchange != "aster" {
|
||||
return fmt.Errorf("trader[%d]: exchange必须是 'binance', 'hyperliquid' 或 'aster'", i)
|
||||
}
|
||||
|
||||
// 根据平台验证对应的密钥
|
||||
if trader.Exchange == "binance" {
|
||||
if trader.BinanceAPIKey == "" || trader.BinanceSecretKey == "" {
|
||||
return fmt.Errorf("trader[%d]: 使用币安时必须配置binance_api_key和binance_secret_key", i)
|
||||
}
|
||||
} else if trader.Exchange == "hyperliquid" {
|
||||
if trader.HyperliquidPrivateKey == "" {
|
||||
return fmt.Errorf("trader[%d]: 使用Hyperliquid时必须配置hyperliquid_private_key", i)
|
||||
}
|
||||
} else if trader.Exchange == "aster" {
|
||||
if trader.AsterUser == "" || trader.AsterSigner == "" || trader.AsterPrivateKey == "" {
|
||||
return fmt.Errorf("trader[%d]: 使用Aster时必须配置aster_user, aster_signer和aster_private_key", i)
|
||||
}
|
||||
}
|
||||
|
||||
if trader.AIModel == "qwen" && trader.QwenKey == "" {
|
||||
return fmt.Errorf("trader[%d]: 使用Qwen时必须配置qwen_key", i)
|
||||
}
|
||||
if trader.AIModel == "deepseek" && trader.DeepSeekKey == "" {
|
||||
return fmt.Errorf("trader[%d]: 使用DeepSeek时必须配置deepseek_key", i)
|
||||
}
|
||||
if trader.AIModel == "custom" {
|
||||
if trader.CustomAPIURL == "" {
|
||||
return fmt.Errorf("trader[%d]: 使用自定义API时必须配置custom_api_url", i)
|
||||
}
|
||||
if trader.CustomAPIKey == "" {
|
||||
return fmt.Errorf("trader[%d]: 使用自定义API时必须配置custom_api_key", i)
|
||||
}
|
||||
if trader.CustomModelName == "" {
|
||||
return fmt.Errorf("trader[%d]: 使用自定义API时必须配置custom_model_name", i)
|
||||
}
|
||||
}
|
||||
if trader.InitialBalance <= 0 {
|
||||
return fmt.Errorf("trader[%d]: initial_balance必须大于0", i)
|
||||
}
|
||||
if trader.ScanIntervalMinutes <= 0 {
|
||||
trader.ScanIntervalMinutes = 3 // 默认3分钟
|
||||
}
|
||||
}
|
||||
|
||||
if c.APIServerPort <= 0 {
|
||||
c.APIServerPort = 8080 // 默认8080端口
|
||||
}
|
||||
|
||||
// 设置杠杆默认值(适配币安子账户限制,最大5倍)
|
||||
if c.Leverage.BTCETHLeverage <= 0 {
|
||||
c.Leverage.BTCETHLeverage = 5 // 默认5倍(安全值,适配子账户)
|
||||
}
|
||||
if c.Leverage.BTCETHLeverage > 5 {
|
||||
fmt.Printf("⚠️ 警告: BTC/ETH杠杆设置为%dx,如果使用子账户可能会失败(子账户限制≤5x)\n", c.Leverage.BTCETHLeverage)
|
||||
}
|
||||
if c.Leverage.AltcoinLeverage <= 0 {
|
||||
c.Leverage.AltcoinLeverage = 5 // 默认5倍(安全值,适配子账户)
|
||||
}
|
||||
if c.Leverage.AltcoinLeverage > 5 {
|
||||
fmt.Printf("⚠️ 警告: 山寨币杠杆设置为%dx,如果使用子账户可能会失败(子账户限制≤5x)\n", c.Leverage.AltcoinLeverage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetScanInterval 获取扫描间隔
|
||||
func (tc *TraderConfig) GetScanInterval() time.Duration {
|
||||
return time.Duration(tc.ScanIntervalMinutes) * time.Minute
|
||||
return &configFile, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user