Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1483,7 +1483,6 @@ func (s *Server) authMiddleware() gin.HandlerFunc {
return
}


tokenString := tokenParts[1]

// 黑名单检查
Expand Down
20 changes: 10 additions & 10 deletions decision/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ type Context struct {

// Decision AI的交易决策
type Decision struct {
Symbol string `json:"symbol"`
Action string `json:"action"` // "open_long", "open_short", "close_long", "close_short", "update_stop_loss", "update_take_profit", "partial_close", "hold", "wait"
Symbol string `json:"symbol"`
Action string `json:"action"` // "open_long", "open_short", "close_long", "close_short", "update_stop_loss", "update_take_profit", "partial_close", "hold", "wait"

// 开仓参数
Leverage int `json:"leverage,omitempty"`
Expand All @@ -92,14 +92,14 @@ type Decision struct {
TakeProfit float64 `json:"take_profit,omitempty"`

// 调整参数(新增)
NewStopLoss float64 `json:"new_stop_loss,omitempty"` // 用于 update_stop_loss
NewTakeProfit float64 `json:"new_take_profit,omitempty"` // 用于 update_take_profit
ClosePercentage float64 `json:"close_percentage,omitempty"` // 用于 partial_close (0-100)
NewStopLoss float64 `json:"new_stop_loss,omitempty"` // 用于 update_stop_loss
NewTakeProfit float64 `json:"new_take_profit,omitempty"` // 用于 update_take_profit
ClosePercentage float64 `json:"close_percentage,omitempty"` // 用于 partial_close (0-100)

// 通用参数
Confidence int `json:"confidence,omitempty"` // 信心度 (0-100)
RiskUSD float64 `json:"risk_usd,omitempty"` // 最大美元风险
Reasoning string `json:"reasoning"`
Confidence int `json:"confidence,omitempty"` // 信心度 (0-100)
RiskUSD float64 `json:"risk_usd,omitempty"` // 最大美元风险
Reasoning string `json:"reasoning"`
}

// FullDecision AI的完整决策(包含思维链)
Expand Down Expand Up @@ -691,8 +691,8 @@ func validateDecision(d *Decision, accountEquity float64, btcEthLeverage, altcoi

// ✅ 验证最小开仓金额(防止数量格式化为 0 的错误)
// Binance 最小名义价值 10 USDT + 安全边际
const minPositionSizeGeneral = 12.0 // 10 + 20% 安全边际
const minPositionSizeBTCETH = 60.0 // BTC/ETH 因价格高和精度限制需要更大金额(更灵活)
const minPositionSizeGeneral = 12.0 // 10 + 20% 安全边际
const minPositionSizeBTCETH = 60.0 // BTC/ETH 因价格高和精度限制需要更大金额(更灵活)

if d.Symbol == "BTCUSDT" || d.Symbol == "ETHUSDT" {
if d.PositionSizeUSD < minPositionSizeBTCETH {
Expand Down
6 changes: 3 additions & 3 deletions logger/telegram_sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ func NewTelegramSender(botToken string, chatID int64) (*TelegramSender, error) {
sender := &TelegramSender{
bot: bot,
chatID: chatID,
msgChan: make(chan string, 20), // 固定缓冲区大小: 20
retryCount: 3, // 固定重试次数: 3
retryInterval: 3 * time.Second, // 固定重试间隔: 3秒
msgChan: make(chan string, 20), // 固定缓冲区大小: 20
retryCount: 3, // 固定重试次数: 3
retryInterval: 3 * time.Second, // 固定重试间隔: 3秒
stopChan: make(chan struct{}),
}

Expand Down
2 changes: 1 addition & 1 deletion mcp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (client *Client) SetQwenAPIKey(apiKey string, customURL string, customModel
client.Model = customModel
log.Printf("🔧 [MCP] Qwen 使用自定义 Model: %s", customModel)
} else {
client.Model = "qwen3-max"
client.Model = "qwen3-max"
log.Printf("🔧 [MCP] Qwen 使用默认 Model: %s", client.Model)
}
// 打印 API Key 的前后各4位用于验证
Expand Down
6 changes: 2 additions & 4 deletions trader/aster_trader.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,9 +484,9 @@ func (t *AsterTrader) GetBalance() (map[string]interface{}, error) {

// 返回与Binance相同的字段名,确保AutoTrader能正确解析
return map[string]interface{}{
"totalWalletBalance": totalBalance, // 钱包余额(不含未实现盈亏)
"totalWalletBalance": totalBalance, // 钱包余额(不含未实现盈亏)
"availableBalance": availableBalance,
"totalUnrealizedProfit": crossUnPnl, // 未实现盈亏
"totalUnrealizedProfit": crossUnPnl, // 未实现盈亏
}, nil
}

Expand Down Expand Up @@ -1010,8 +1010,6 @@ func (t *AsterTrader) SetTakeProfit(symbol string, positionSide string, quantity
return err
}



// CancelStopLossOrders 仅取消止损单(不影响止盈单)
func (t *AsterTrader) CancelStopLossOrders(symbol string) error {
// 获取该币种的所有未完成订单
Expand Down
36 changes: 18 additions & 18 deletions trader/auto_trader.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,16 @@ type AutoTrader struct {
lastResetTime time.Time
stopUntil time.Time
isRunning bool
startTime time.Time // 系统启动时间
callCount int // AI调用次数
positionFirstSeenTime map[string]int64 // 持仓首次出现时间 (symbol_side -> timestamp毫秒)
stopMonitorCh chan struct{} // 用于停止监控goroutine
monitorWg sync.WaitGroup // 用于等待监控goroutine结束
peakPnLCache map[string]float64 // 最高收益缓存 (symbol -> 峰值盈亏百分比)
peakPnLCacheMutex sync.RWMutex // 缓存读写锁
lastBalanceSyncTime time.Time // 上次余额同步时间
database interface{} // 数据库引用(用于自动更新余额)
userID string // 用户ID
startTime time.Time // 系统启动时间
callCount int // AI调用次数
positionFirstSeenTime map[string]int64 // 持仓首次出现时间 (symbol_side -> timestamp毫秒)
stopMonitorCh chan struct{} // 用于停止监控goroutine
monitorWg sync.WaitGroup // 用于等待监控goroutine结束
peakPnLCache map[string]float64 // 最高收益缓存 (symbol -> 峰值盈亏百分比)
peakPnLCacheMutex sync.RWMutex // 缓存读写锁
lastBalanceSyncTime time.Time // 上次余额同步时间
database interface{} // 数据库引用(用于自动更新余额)
userID string // 用户ID
}

// NewAutoTrader 创建自动交易器
Expand Down Expand Up @@ -436,7 +436,7 @@ func (at *AutoTrader) runCycle() error {
})
}

log.Print(strings.Repeat("=", 70))
log.Print(strings.Repeat("=", 70))
for _, coin := range ctx.CandidateCoins {
record.CandidateCoins = append(record.CandidateCoins, coin.Symbol)
}
Expand Down Expand Up @@ -465,11 +465,11 @@ func (at *AutoTrader) runCycle() error {

// 打印系统提示词和AI思维链(即使有错误,也要输出以便调试)
if decision != nil {
log.Print("\n" + strings.Repeat("=", 70) + "\n")
log.Printf("📋 系统提示词 [模板: %s] (错误情况)", at.systemPromptTemplate)
log.Println(strings.Repeat("=", 70))
log.Println(decision.SystemPrompt)
log.Println(strings.Repeat("=", 70))
log.Print("\n" + strings.Repeat("=", 70) + "\n")
log.Printf("📋 系统提示词 [模板: %s] (错误情况)", at.systemPromptTemplate)
log.Println(strings.Repeat("=", 70))
log.Println(decision.SystemPrompt)
log.Println(strings.Repeat("=", 70))

if decision.CoTTrace != "" {
log.Print("\n" + strings.Repeat("-", 70) + "\n")
Expand Down Expand Up @@ -508,9 +508,9 @@ func (at *AutoTrader) runCycle() error {
// }
// }
log.Println()
log.Print(strings.Repeat("-", 70))
log.Print(strings.Repeat("-", 70))
// 8. 对决策排序:确保先平仓后开仓(防止仓位叠加超限)
log.Print(strings.Repeat("-", 70))
log.Print(strings.Repeat("-", 70))

// 8. 对决策排序:确保先平仓后开仓(防止仓位叠加超限)
sortedDecisions := sortDecisionsByPriority(decision.Decisions)
Expand Down
2 changes: 0 additions & 2 deletions trader/binance_futures.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,6 @@ func (t *FuturesTrader) CloseShort(symbol string, quantity float64) (map[string]
return result, nil
}



// CancelStopLossOrders 仅取消止损单(不影响止盈单)
func (t *FuturesTrader) CancelStopLossOrders(symbol string) error {
// 获取该币种的所有未完成订单
Expand Down
9 changes: 4 additions & 5 deletions trader/hyperliquid_trader.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ func (t *HyperliquidTrader) GetBalance() (map[string]interface{}, error) {
// 原因:Spot 和 Perpetuals 是独立帐户,需手动 ClassTransfer 才能转账
totalWalletBalance := walletBalanceWithoutUnrealized + spotUSDCBalance

result["totalWalletBalance"] = totalWalletBalance // 总资产(Perp + Spot)
result["availableBalance"] = availableBalance // 可用余额(仅 Perpetuals,不含 Spot)
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未实现盈亏(仅来自 Perpetuals)
result["spotBalance"] = spotUSDCBalance // Spot 现货余额(单独返回)
result["totalWalletBalance"] = totalWalletBalance // 总资产(Perp + Spot)
result["availableBalance"] = availableBalance // 可用余额(仅 Perpetuals,不含 Spot)
result["totalUnrealizedProfit"] = totalUnrealizedPnl // 未实现盈亏(仅来自 Perpetuals)
result["spotBalance"] = spotUSDCBalance // Spot 现货余额(单独返回)

log.Printf("✓ Hyperliquid 完整账户:")
log.Printf(" • Spot 现货余额: %.2f USDC (需手动转账到 Perpetuals 才能开仓)", spotUSDCBalance)
Expand Down Expand Up @@ -551,7 +551,6 @@ func (t *HyperliquidTrader) CloseShort(symbol string, quantity float64) (map[str

// CancelStopOrders 取消该币种的止盈/止


// CancelStopLossOrders 仅取消止损单(Hyperliquid 暂无法区分止损和止盈,取消所有)
func (t *HyperliquidTrader) CancelStopLossOrders(symbol string) error {
// Hyperliquid SDK 的 OpenOrder 结构不暴露 trigger 字段
Expand Down
10 changes: 5 additions & 5 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function App() {

// 如果在trader页面,获取该trader的数据
const { data: status } = useSWR<SystemStatus>(
currentPage === 'trader' && selectedTraderId
user && token && currentPage === 'trader' && selectedTraderId
? `status-${selectedTraderId}`
: null,
() => api.getStatus(selectedTraderId),
Expand All @@ -130,7 +130,7 @@ function App() {
)

const { data: account } = useSWR<AccountInfo>(
currentPage === 'trader' && selectedTraderId
user && token && currentPage === 'trader' && selectedTraderId
? `account-${selectedTraderId}`
: null,
() => api.getAccount(selectedTraderId),
Expand All @@ -142,7 +142,7 @@ function App() {
)

const { data: positions } = useSWR<Position[]>(
currentPage === 'trader' && selectedTraderId
user && token && currentPage === 'trader' && selectedTraderId
? `positions-${selectedTraderId}`
: null,
() => api.getPositions(selectedTraderId),
Expand All @@ -154,7 +154,7 @@ function App() {
)

const { data: decisions } = useSWR<DecisionRecord[]>(
currentPage === 'trader' && selectedTraderId
user && token && currentPage === 'trader' && selectedTraderId
? `decisions/latest-${selectedTraderId}`
: null,
() => api.getLatestDecisions(selectedTraderId),
Expand All @@ -166,7 +166,7 @@ function App() {
)

const { data: stats } = useSWR<Statistics>(
currentPage === 'trader' && selectedTraderId
user && token && currentPage === 'trader' && selectedTraderId
? `statistics-${selectedTraderId}`
: null,
() => api.getStatistics(selectedTraderId),
Expand Down
6 changes: 4 additions & 2 deletions web/src/components/EquityChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import useSWR from 'swr'
import { api } from '../lib/api'
import { useLanguage } from '../contexts/LanguageContext'
import { useAuth } from '../contexts/AuthContext'
import { t } from '../i18n/translations'
import {
AlertTriangle,
Expand All @@ -36,10 +37,11 @@ interface EquityChartProps {

export function EquityChart({ traderId }: EquityChartProps) {
const { language } = useLanguage()
const { user, token } = useAuth()
const [displayMode, setDisplayMode] = useState<'dollar' | 'percent'>('dollar')

const { data: history, error } = useSWR<EquityPoint[]>(
traderId ? `equity-history-${traderId}` : 'equity-history',
user && token && traderId ? `equity-history-${traderId}` : null,
() => api.getEquityHistory(traderId),
{
refreshInterval: 30000, // 30秒刷新(历史数据更新频率较低)
Expand All @@ -49,7 +51,7 @@ export function EquityChart({ traderId }: EquityChartProps) {
)

const { data: account } = useSWR(
traderId ? `account-${traderId}` : 'account',
user && token && traderId ? `account-${traderId}` : null,
() => api.getAccount(traderId),
{
refreshInterval: 15000, // 15秒刷新(配合后端缓存)
Expand Down
Loading