mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-06 12:30:59 +08:00
feat(config): 增加新闻源配置与数据库迁移支持
- 在config.json.example中添加新闻源相关配置示例,支持telegram频道订阅 - 扩展数据库表结构,新增traders表多字段支持信号源和杠杆参数 - 新增NewsConfig结构体及相关telegram新闻配置数据模型 - 在交易决策上下文结构Context中添加新闻数据news字段支持传递新闻 - 在交易决策构建用户提示信息时加入相关新闻内容 - 优化数据库操作代码,支持交易所和交易员配置的完整字段读取与更新 - 添加数据库exchanges表迁移逻辑,重建表结构和触发器以支持新字段 - 引入第三方库github.com/samber/lo用于集合操作 - 在go.mod添加新的依赖模块,并更新相关依赖版本
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"nofx/config"
|
||||
"nofx/news"
|
||||
"nofx/trader"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -83,8 +84,31 @@ func (tm *TraderManager) LoadTradersFromDatabase(database *config.Database) erro
|
||||
}
|
||||
}
|
||||
|
||||
// 获取新闻源配置
|
||||
var newsCfg []config.NewsConfig
|
||||
newsConfStr, _ := database.GetSystemConfig("news_config")
|
||||
if newsConfStr != "" {
|
||||
if err := json.Unmarshal([]byte(newsConfStr), &newsCfg); err != nil {
|
||||
log.Printf("⚠️ 解析新闻源配置失败: %v,使用空列表", err)
|
||||
newsCfg = []config.NewsConfig{}
|
||||
}
|
||||
|
||||
// 新闻配置处理
|
||||
for index, newsConf := range newsCfg {
|
||||
switch newsConf.Provider {
|
||||
case news.ProviderTelegram:
|
||||
if newsConf.Telegram.BaseURL == "" {
|
||||
newsCfg[index].Telegram.BaseURL = "https://t.me/s"
|
||||
}
|
||||
if len(newsConf.TelegramChannel) == 0 {
|
||||
newsCfg[index].TelegramChannel = append(newsCfg[index].TelegramChannel, config.NewsConfigTelegramChannel{ID: "ChannelPANews", Name: "PANews"})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 为每个交易员获取AI模型和交易所配置
|
||||
for _, traderCfg := range allTraders {
|
||||
for _, traderCfg := range allTraders {
|
||||
// 获取AI模型配置(使用交易员所属的用户ID)
|
||||
aiModels, err := database.GetAIModels(traderCfg.UserID)
|
||||
if err != nil {
|
||||
@@ -157,7 +181,7 @@ func (tm *TraderManager) LoadTradersFromDatabase(database *config.Database) erro
|
||||
}
|
||||
|
||||
// 添加到TraderManager
|
||||
err = tm.addTraderFromDB(traderCfg, aiModelCfg, exchangeCfg, coinPoolURL, oiTopURL, maxDailyLoss, maxDrawdown, stopTradingMinutes, defaultCoins)
|
||||
err = tm.addTraderFromDB(traderCfg, aiModelCfg, exchangeCfg, newsCfg, coinPoolURL, oiTopURL, maxDailyLoss, maxDrawdown, stopTradingMinutes, defaultCoins)
|
||||
if err != nil {
|
||||
log.Printf("❌ 添加交易员 %s 失败: %v", traderCfg.Name, err)
|
||||
continue
|
||||
@@ -169,7 +193,7 @@ func (tm *TraderManager) LoadTradersFromDatabase(database *config.Database) erro
|
||||
}
|
||||
|
||||
// addTraderFromConfig 内部方法:从配置添加交易员(不加锁,因为调用方已加锁)
|
||||
func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModelCfg *config.AIModelConfig, exchangeCfg *config.ExchangeConfig, coinPoolURL, oiTopURL string, maxDailyLoss, maxDrawdown float64, stopTradingMinutes int, defaultCoins []string) error {
|
||||
func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModelCfg *config.AIModelConfig, exchangeCfg *config.ExchangeConfig, newsCfg []config.NewsConfig, coinPoolURL, oiTopURL string, maxDailyLoss, maxDrawdown float64, stopTradingMinutes int, defaultCoins []string) error {
|
||||
if _, exists := tm.traders[traderCfg.ID]; exists {
|
||||
return fmt.Errorf("trader ID '%s' 已存在", traderCfg.ID)
|
||||
}
|
||||
@@ -186,7 +210,7 @@ func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果没有指定交易币种,使用默认币种
|
||||
if len(tradingCoins) == 0 {
|
||||
tradingCoins = defaultCoins
|
||||
@@ -200,7 +224,7 @@ func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModel
|
||||
}
|
||||
|
||||
// 构建AutoTraderConfig
|
||||
traderConfig := trader.AutoTraderConfig{
|
||||
traderConfig := trader.AutoTraderConfig{
|
||||
ID: traderCfg.ID,
|
||||
Name: traderCfg.Name,
|
||||
AIModel: aiModelCfg.Provider, // 使用provider作为模型标识
|
||||
@@ -226,6 +250,7 @@ func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModel
|
||||
DefaultCoins: defaultCoins,
|
||||
TradingCoins: tradingCoins,
|
||||
SystemPromptTemplate: traderCfg.SystemPromptTemplate, // 系统提示词模板
|
||||
NewsConfig: newsCfg, // 新闻源配置
|
||||
}
|
||||
|
||||
// 根据交易所类型设置API密钥
|
||||
@@ -253,7 +278,7 @@ func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModel
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建trader失败: %w", err)
|
||||
}
|
||||
|
||||
|
||||
// 设置自定义prompt(如果有)
|
||||
if traderCfg.CustomPrompt != "" {
|
||||
at.SetCustomPrompt(traderCfg.CustomPrompt)
|
||||
@@ -293,7 +318,7 @@ func (tm *TraderManager) AddTraderFromDB(traderCfg *config.TraderRecord, aiModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果没有指定交易币种,使用默认币种
|
||||
if len(tradingCoins) == 0 {
|
||||
tradingCoins = defaultCoins
|
||||
@@ -359,7 +384,7 @@ func (tm *TraderManager) AddTraderFromDB(traderCfg *config.TraderRecord, aiModel
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建trader失败: %w", err)
|
||||
}
|
||||
|
||||
|
||||
// 设置自定义prompt(如果有)
|
||||
if traderCfg.CustomPrompt != "" {
|
||||
at.SetCustomPrompt(traderCfg.CustomPrompt)
|
||||
@@ -488,9 +513,9 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
|
||||
for _, t := range tm.traders {
|
||||
account, err := t.GetAccountInfo()
|
||||
status := t.GetStatus()
|
||||
|
||||
|
||||
var traderData map[string]interface{}
|
||||
|
||||
|
||||
if err != nil {
|
||||
// 如果获取账户信息失败,使用默认值但仍然显示交易员
|
||||
log.Printf("⚠️ 获取交易员 %s 账户信息失败: %v", t.GetID(), err)
|
||||
@@ -522,7 +547,7 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
|
||||
"is_running": status["is_running"],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
traders = append(traders, traderData)
|
||||
}
|
||||
comparison["traders"] = traders
|
||||
@@ -708,7 +733,7 @@ func (tm *TraderManager) loadSingleTrader(traderCfg *config.TraderRecord, aiMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果没有指定交易币种,使用默认币种
|
||||
if len(tradingCoins) == 0 {
|
||||
tradingCoins = defaultCoins
|
||||
@@ -723,25 +748,25 @@ func (tm *TraderManager) loadSingleTrader(traderCfg *config.TraderRecord, aiMode
|
||||
|
||||
// 构建AutoTraderConfig
|
||||
traderConfig := trader.AutoTraderConfig{
|
||||
ID: traderCfg.ID,
|
||||
Name: traderCfg.Name,
|
||||
AIModel: aiModelCfg.Provider, // 使用provider作为模型标识
|
||||
Exchange: exchangeCfg.ID, // 使用exchange ID
|
||||
InitialBalance: traderCfg.InitialBalance,
|
||||
BTCETHLeverage: traderCfg.BTCETHLeverage,
|
||||
AltcoinLeverage: traderCfg.AltcoinLeverage,
|
||||
ScanInterval: time.Duration(traderCfg.ScanIntervalMinutes) * time.Minute,
|
||||
CoinPoolAPIURL: effectiveCoinPoolURL,
|
||||
CustomAPIURL: aiModelCfg.CustomAPIURL, // 自定义API URL
|
||||
CustomModelName: aiModelCfg.CustomModelName, // 自定义模型名称
|
||||
UseQwen: aiModelCfg.Provider == "qwen",
|
||||
MaxDailyLoss: maxDailyLoss,
|
||||
MaxDrawdown: maxDrawdown,
|
||||
StopTradingTime: time.Duration(stopTradingMinutes) * time.Minute,
|
||||
IsCrossMargin: traderCfg.IsCrossMargin,
|
||||
DefaultCoins: defaultCoins,
|
||||
TradingCoins: tradingCoins,
|
||||
SystemPromptTemplate: traderCfg.SystemPromptTemplate, // 系统提示词模板
|
||||
ID: traderCfg.ID,
|
||||
Name: traderCfg.Name,
|
||||
AIModel: aiModelCfg.Provider, // 使用provider作为模型标识
|
||||
Exchange: exchangeCfg.ID, // 使用exchange ID
|
||||
InitialBalance: traderCfg.InitialBalance,
|
||||
BTCETHLeverage: traderCfg.BTCETHLeverage,
|
||||
AltcoinLeverage: traderCfg.AltcoinLeverage,
|
||||
ScanInterval: time.Duration(traderCfg.ScanIntervalMinutes) * time.Minute,
|
||||
CoinPoolAPIURL: effectiveCoinPoolURL,
|
||||
CustomAPIURL: aiModelCfg.CustomAPIURL, // 自定义API URL
|
||||
CustomModelName: aiModelCfg.CustomModelName, // 自定义模型名称
|
||||
UseQwen: aiModelCfg.Provider == "qwen",
|
||||
MaxDailyLoss: maxDailyLoss,
|
||||
MaxDrawdown: maxDrawdown,
|
||||
StopTradingTime: time.Duration(stopTradingMinutes) * time.Minute,
|
||||
IsCrossMargin: traderCfg.IsCrossMargin,
|
||||
DefaultCoins: defaultCoins,
|
||||
TradingCoins: tradingCoins,
|
||||
SystemPromptTemplate: traderCfg.SystemPromptTemplate, // 系统提示词模板
|
||||
}
|
||||
|
||||
// 根据交易所类型设置API密钥
|
||||
@@ -769,7 +794,7 @@ func (tm *TraderManager) loadSingleTrader(traderCfg *config.TraderRecord, aiMode
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建trader失败: %w", err)
|
||||
}
|
||||
|
||||
|
||||
// 设置自定义prompt(如果有)
|
||||
if traderCfg.CustomPrompt != "" {
|
||||
at.SetCustomPrompt(traderCfg.CustomPrompt)
|
||||
|
||||
Reference in New Issue
Block a user