mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-16 09:11:03 +08:00
feat(trader): add automatic balance sync every 30 minutes
## Summary Automatically sync trader's initial_balance when exchange balance changes significantly (>5%), eliminating the need for manual intervention after deposits/withdrawals. ## Changes ### trader/auto_trader.go - Add fields: lastBalanceSyncTime, database, userID - Add autoSyncBalanceIfNeeded() method: * Check every 30 minutes (配合3分钟扫描周期,约10次扫描触发一次) * Query exchange balance via trader.GetBalance() * Update if change >5% * Log changes with emoji indicators - Integrate into runCycle() before trading decisions ### manager/trader_manager.go - Update NewAutoTrader() calls to pass database and userID - Update method signatures: * addTraderFromDB() * AddTraderFromDB() * loadSingleTrader() ## Configuration - Check interval: 30 minutes - Change threshold: 5% - Automatic detection and update ## Benefits ✅ 完全自动化,无需手动调用API ✅ 30分钟检查间隔,及时又不频繁 ✅ 智能判断(变化>5%才更新) ✅ 充值/提现后自动更新initial_balance ✅ 前端P&L显示自动修正 ## Example Logs 🔄 [DS-BF] 开始自动检查余额变化... 🔔 [DS-BF] 检测到余额大幅变化: 693.00 → 3000.00 USDT (333.33%) ✅ [DS-BF] 已自动同步余额到数据库 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -118,10 +118,13 @@ type AutoTrader struct {
|
||||
positionFirstSeenTime map[string]int64 // 持仓首次出现时间 (symbol_side -> timestamp毫秒)
|
||||
lastPositions map[string]*PositionSnapshot // 上一个周期的持仓快照 (symbol_side -> snapshot)
|
||||
newsProcessor []news.Provider // 新闻
|
||||
lastBalanceSyncTime time.Time // 上次余额同步时间
|
||||
database *config.Database // 数据库引用(用于自动更新余额)
|
||||
userID string // 用户ID
|
||||
}
|
||||
|
||||
// NewAutoTrader 创建自动交易器
|
||||
func NewAutoTrader(traderConfig AutoTraderConfig) (*AutoTrader, error) {
|
||||
func NewAutoTrader(traderConfig AutoTraderConfig, db *config.Database, userID string) (*AutoTrader, error) {
|
||||
// 设置默认值
|
||||
if traderConfig.ID == "" {
|
||||
traderConfig.ID = "default_trader"
|
||||
@@ -261,6 +264,9 @@ func NewAutoTrader(traderConfig AutoTraderConfig) (*AutoTrader, error) {
|
||||
positionFirstSeenTime: make(map[string]int64),
|
||||
lastPositions: make(map[string]*PositionSnapshot),
|
||||
newsProcessor: newsProcessor,
|
||||
lastBalanceSyncTime: time.Now(), // 初始化为当前时间
|
||||
database: db,
|
||||
userID: userID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -298,6 +304,64 @@ func (at *AutoTrader) Stop() {
|
||||
log.Println("⏹ 自动交易系统停止")
|
||||
}
|
||||
|
||||
// autoSyncBalanceIfNeeded 自动同步余额(每30分钟检查一次,变化>5%才更新)
|
||||
func (at *AutoTrader) autoSyncBalanceIfNeeded() {
|
||||
// 距离上次同步不足30分钟,跳过
|
||||
if time.Since(at.lastBalanceSyncTime) < 30*time.Minute {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("🔄 [%s] 开始自动检查余额变化...", at.name)
|
||||
|
||||
// 查询实际余额
|
||||
balanceInfo, err := at.trader.GetBalance()
|
||||
if err != nil {
|
||||
log.Printf("⚠️ [%s] 查询余额失败: %v", at.name, err)
|
||||
at.lastBalanceSyncTime = time.Now() // 即使失败也更新时间,避免频繁重试
|
||||
return
|
||||
}
|
||||
|
||||
// 提取可用余额
|
||||
var actualBalance float64
|
||||
if availableBalance, ok := balanceInfo["available_balance"].(float64); ok && availableBalance > 0 {
|
||||
actualBalance = availableBalance
|
||||
} else if availableBalance, ok := balanceInfo["availableBalance"].(float64); ok && availableBalance > 0 {
|
||||
actualBalance = availableBalance
|
||||
} else if totalBalance, ok := balanceInfo["balance"].(float64); ok && totalBalance > 0 {
|
||||
actualBalance = totalBalance
|
||||
} else {
|
||||
log.Printf("⚠️ [%s] 无法提取可用余额", at.name)
|
||||
at.lastBalanceSyncTime = time.Now()
|
||||
return
|
||||
}
|
||||
|
||||
oldBalance := at.initialBalance
|
||||
changePercent := ((actualBalance - oldBalance) / oldBalance) * 100
|
||||
|
||||
// 变化超过5%才更新
|
||||
if math.Abs(changePercent) > 5.0 {
|
||||
log.Printf("🔔 [%s] 检测到余额大幅变化: %.2f → %.2f USDT (%.2f%%)",
|
||||
at.name, oldBalance, actualBalance, changePercent)
|
||||
|
||||
// 更新内存中的 initialBalance
|
||||
at.initialBalance = actualBalance
|
||||
|
||||
// 更新数据库
|
||||
if at.database != nil {
|
||||
err := at.database.UpdateTraderInitialBalance(at.userID, at.id, actualBalance)
|
||||
if err != nil {
|
||||
log.Printf("❌ [%s] 更新数据库失败: %v", at.name, err)
|
||||
} else {
|
||||
log.Printf("✅ [%s] 已自动同步余额到数据库", at.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("✓ [%s] 余额变化不大 (%.2f%%),无需更新", at.name, changePercent)
|
||||
}
|
||||
|
||||
at.lastBalanceSyncTime = time.Now()
|
||||
}
|
||||
|
||||
// runCycle 运行一个交易周期(使用AI全权决策)
|
||||
func (at *AutoTrader) runCycle() error {
|
||||
at.callCount++
|
||||
@@ -329,7 +393,10 @@ func (at *AutoTrader) runCycle() error {
|
||||
log.Println("📅 日盈亏已重置")
|
||||
}
|
||||
|
||||
// 3. 收集交易上下文
|
||||
// 3. 自动同步余额(每30分钟检查一次,充值/提现后自动更新)
|
||||
at.autoSyncBalanceIfNeeded()
|
||||
|
||||
// 4. 收集交易上下文
|
||||
ctx, err := at.buildTradingContext()
|
||||
if err != nil {
|
||||
record.Success = false
|
||||
|
||||
Reference in New Issue
Block a user