mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-04 11:30:58 +08:00
**Configuration Improvements:** - Add comprehensive "Get AI API Keys" section (DeepSeek + Qwen) - Split configuration into Beginner Mode and Expert Mode - Add step-by-step setup guides with placeholder tables - Add configuration checklist and field explanations - Document smart default behavior for use_default_coins (v2.0.2) **Startup/Monitoring Guides:** - Add detailed 2-step system startup instructions - Include expected output and error troubleshooting tables - Add monitoring and graceful shutdown procedures - Provide health check commands **AI Decision Flow Updates:** - Enhance flow diagram with emojis and wider boxes - Add v2.0.2 improvement markers throughout - Document position duration tracking - Document accurate USDT PnL calculation with leverage - Document enhanced AI freedom for raw data analysis - Document improved position tracking (symbol_side keys) - Add "Key Improvements in v2.0.2" summary section **Code Changes:** - Simplify config.json.example to beginner-friendly single trader - Implement smart default: use_default_coins auto-enables when omitted - Set default to true if false without coin_pool_api_url **Files Updated:** - README.md (English): +559 lines - README.zh-CN.md (Chinese): +527 lines - README.ru.md (Russian): +634 lines - README.uk.md (Ukrainian): +521 lines - config.json.example: Simplified configuration - config/config.go: Smart default logic Total: 2068+ additions across all documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
110 lines
3.4 KiB
Go
110 lines
3.4 KiB
Go
package config
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"time"
|
||
)
|
||
|
||
// TraderConfig 单个trader的配置
|
||
type TraderConfig struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
AIModel string `json:"ai_model"` // "qwen" or "deepseek"
|
||
BinanceAPIKey string `json:"binance_api_key"`
|
||
BinanceSecretKey string `json:"binance_secret_key"`
|
||
QwenKey string `json:"qwen_key,omitempty"`
|
||
DeepSeekKey string `json:"deepseek_key,omitempty"`
|
||
InitialBalance float64 `json:"initial_balance"`
|
||
ScanIntervalMinutes int `json:"scan_interval_minutes"`
|
||
}
|
||
|
||
// Config 总配置
|
||
type Config struct {
|
||
Traders []TraderConfig `json:"traders"`
|
||
UseDefaultCoins bool `json:"use_default_coins"` // 是否使用默认主流币种列表
|
||
CoinPoolAPIURL string `json:"coin_pool_api_url"`
|
||
OITopAPIURL string `json:"oi_top_api_url"`
|
||
APIServerPort int `json:"api_server_port"`
|
||
MaxDailyLoss float64 `json:"max_daily_loss"`
|
||
MaxDrawdown float64 `json:"max_drawdown"`
|
||
StopTradingMinutes int `json:"stop_trading_minutes"`
|
||
}
|
||
|
||
// LoadConfig 从文件加载配置
|
||
func LoadConfig(filename string) (*Config, error) {
|
||
data, err := os.ReadFile(filename)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("读取配置文件失败: %w", err)
|
||
}
|
||
|
||
var config Config
|
||
if err := json.Unmarshal(data, &config); err != nil {
|
||
return nil, fmt.Errorf("解析配置文件失败: %w", err)
|
||
}
|
||
|
||
// 设置默认值:如果use_default_coins未设置(为false)且没有配置coin_pool_api_url,则默认使用默认币种列表
|
||
if !config.UseDefaultCoins && config.CoinPoolAPIURL == "" {
|
||
config.UseDefaultCoins = true
|
||
}
|
||
|
||
// 验证配置
|
||
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" {
|
||
return fmt.Errorf("trader[%d]: ai_model必须是 'qwen' 或 'deepseek'", i)
|
||
}
|
||
if trader.BinanceAPIKey == "" || trader.BinanceSecretKey == "" {
|
||
return fmt.Errorf("trader[%d]: 币安API密钥不能为空", 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.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端口
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GetScanInterval 获取扫描间隔
|
||
func (tc *TraderConfig) GetScanInterval() time.Duration {
|
||
return time.Duration(tc.ScanIntervalMinutes) * time.Minute
|
||
}
|