mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-09 05:51:01 +08:00
feat: cream terminal redesign, English-only UI, autopilot launch fixes
- Redesign dashboard into a cream-paper + vermilion IBM Plex Mono terminal (live L2 order book, cost/liq map, WS K-line, signal matrix, orchestration topology, risk radar, execution log, current positions, equity curve) - Convert all user-facing UI and backend strings/prompts from Chinese to English (multi-language retained, default English) - Add /api/statistics/full endpoint + full-stats frontend wiring - Fix Autopilot launch: reuse the existing trader instead of creating duplicates (eliminates repeat ~35s create cost and stale-trader 404s); launch sends 5m scan interval - Fix unreadable toasts: cream theme with high-contrast text + per-type accent - Silence background dashboard polls (getTraderConfig) to stop error-toast spam
This commit is contained in:
@@ -17,24 +17,24 @@ import (
|
||||
|
||||
// GridLevelInfo represents a single grid level's current state
|
||||
type GridLevelInfo struct {
|
||||
Index int `json:"index"` // Level index (0 = lowest)
|
||||
Price float64 `json:"price"` // Target price for this level
|
||||
State string `json:"state"` // "empty", "pending", "filled"
|
||||
Side string `json:"side"` // "buy" or "sell"
|
||||
OrderID string `json:"order_id"` // Current order ID (if pending)
|
||||
OrderQuantity float64 `json:"order_quantity"` // Order quantity
|
||||
PositionSize float64 `json:"position_size"` // Position size (if filled)
|
||||
PositionEntry float64 `json:"position_entry"` // Entry price (if filled)
|
||||
AllocatedUSD float64 `json:"allocated_usd"` // USD allocated to this level
|
||||
UnrealizedPnL float64 `json:"unrealized_pnl"` // Unrealized P&L (if filled)
|
||||
Index int `json:"index"` // Level index (0 = lowest)
|
||||
Price float64 `json:"price"` // Target price for this level
|
||||
State string `json:"state"` // "empty", "pending", "filled"
|
||||
Side string `json:"side"` // "buy" or "sell"
|
||||
OrderID string `json:"order_id"` // Current order ID (if pending)
|
||||
OrderQuantity float64 `json:"order_quantity"` // Order quantity
|
||||
PositionSize float64 `json:"position_size"` // Position size (if filled)
|
||||
PositionEntry float64 `json:"position_entry"` // Entry price (if filled)
|
||||
AllocatedUSD float64 `json:"allocated_usd"` // USD allocated to this level
|
||||
UnrealizedPnL float64 `json:"unrealized_pnl"` // Unrealized P&L (if filled)
|
||||
}
|
||||
|
||||
// GridContext contains all information needed for AI grid decision making
|
||||
type GridContext struct {
|
||||
// Basic info
|
||||
Symbol string `json:"symbol"`
|
||||
CurrentTime string `json:"current_time"`
|
||||
CurrentPrice float64 `json:"current_price"`
|
||||
Symbol string `json:"symbol"`
|
||||
CurrentTime string `json:"current_time"`
|
||||
CurrentPrice float64 `json:"current_price"`
|
||||
|
||||
// Grid configuration
|
||||
GridCount int `json:"grid_count"`
|
||||
@@ -52,22 +52,22 @@ type GridContext struct {
|
||||
IsPaused bool `json:"is_paused"`
|
||||
|
||||
// Market data
|
||||
ATR14 float64 `json:"atr14"`
|
||||
BollingerUpper float64 `json:"bollinger_upper"`
|
||||
ATR14 float64 `json:"atr14"`
|
||||
BollingerUpper float64 `json:"bollinger_upper"`
|
||||
BollingerMiddle float64 `json:"bollinger_middle"`
|
||||
BollingerLower float64 `json:"bollinger_lower"`
|
||||
BollingerWidth float64 `json:"bollinger_width"` // Percentage
|
||||
EMA20 float64 `json:"ema20"`
|
||||
EMA50 float64 `json:"ema50"`
|
||||
EMADistance float64 `json:"ema_distance"` // Percentage
|
||||
RSI14 float64 `json:"rsi14"`
|
||||
MACD float64 `json:"macd"`
|
||||
MACDSignal float64 `json:"macd_signal"`
|
||||
MACDHistogram float64 `json:"macd_histogram"`
|
||||
FundingRate float64 `json:"funding_rate"`
|
||||
Volume24h float64 `json:"volume_24h"`
|
||||
PriceChange1h float64 `json:"price_change_1h"`
|
||||
PriceChange4h float64 `json:"price_change_4h"`
|
||||
BollingerLower float64 `json:"bollinger_lower"`
|
||||
BollingerWidth float64 `json:"bollinger_width"` // Percentage
|
||||
EMA20 float64 `json:"ema20"`
|
||||
EMA50 float64 `json:"ema50"`
|
||||
EMADistance float64 `json:"ema_distance"` // Percentage
|
||||
RSI14 float64 `json:"rsi14"`
|
||||
MACD float64 `json:"macd"`
|
||||
MACDSignal float64 `json:"macd_signal"`
|
||||
MACDHistogram float64 `json:"macd_histogram"`
|
||||
FundingRate float64 `json:"funding_rate"`
|
||||
Volume24h float64 `json:"volume_24h"`
|
||||
PriceChange1h float64 `json:"price_change_1h"`
|
||||
PriceChange4h float64 `json:"price_change_4h"`
|
||||
|
||||
// Account info
|
||||
TotalEquity float64 `json:"total_equity"`
|
||||
@@ -102,53 +102,53 @@ func BuildGridSystemPrompt(config *store.GridStrategyConfig, lang string) string
|
||||
}
|
||||
|
||||
func buildGridSystemPromptZh(config *store.GridStrategyConfig) string {
|
||||
return fmt.Sprintf(`# 你是一个专业的网格交易AI
|
||||
return fmt.Sprintf(`# You are a Professional Grid Trading AI
|
||||
|
||||
## 角色定义
|
||||
你是一个经验丰富的网格交易专家,负责管理 %s 的网格交易策略。你的任务是:
|
||||
1. 判断当前市场状态(震荡/趋势/高波动)
|
||||
2. 决定是否需要调整网格或暂停交易
|
||||
3. 管理每个网格层级的订单
|
||||
## Role Definition
|
||||
You are an experienced grid trading expert responsible for managing the grid trading strategy for %s. Your tasks are:
|
||||
1. Determine the current market state (ranging/trending/high volatility)
|
||||
2. Decide whether to adjust the grid or pause trading
|
||||
3. Manage orders at each grid level
|
||||
|
||||
## 网格配置
|
||||
- 交易对: %s
|
||||
- 网格层数: %d
|
||||
- 总投资: %.2f USDT
|
||||
- 杠杆: %dx
|
||||
- 价格分布: %s
|
||||
## Grid Configuration
|
||||
- Trading Pair: %s
|
||||
- Grid Levels: %d
|
||||
- Total Investment: %.2f USDT
|
||||
- Leverage: %dx
|
||||
- Price Distribution: %s
|
||||
|
||||
## 决策规则
|
||||
## Decision Rules
|
||||
|
||||
### 市场状态判断
|
||||
- **震荡市场** (适合网格): 布林带宽度 < 3%%, EMA20/50 距离 < 1%%, 价格在布林带中轨附近
|
||||
- **趋势市场** (暂停网格): 布林带宽度 > 4%%, EMA20/50 距离 > 2%%, 价格持续突破布林带
|
||||
- **高波动市场** (谨慎): ATR异常放大, 价格剧烈波动
|
||||
### Market State Judgment
|
||||
- **Ranging Market** (suitable for grid): Bollinger band width < 3%%, EMA20/50 distance < 1%%, price near the Bollinger middle band
|
||||
- **Trending Market** (pause grid): Bollinger band width > 4%%, EMA20/50 distance > 2%%, price continuously breaking out of the Bollinger bands
|
||||
- **High Volatility Market** (caution): abnormally expanding ATR, sharp price swings
|
||||
|
||||
### 可执行的操作
|
||||
- place_buy_limit: 在指定价格下买入限价单
|
||||
- place_sell_limit: 在指定价格下卖出限价单
|
||||
- cancel_order: 取消指定订单
|
||||
- cancel_all_orders: 取消所有订单
|
||||
- pause_grid: 暂停网格交易(趋势市场时)
|
||||
- resume_grid: 恢复网格交易(震荡市场时)
|
||||
- adjust_grid: 调整网格边界
|
||||
- hold: 保持当前状态不操作
|
||||
### Available Actions
|
||||
- place_buy_limit: place a buy limit order at the specified price
|
||||
- place_sell_limit: place a sell limit order at the specified price
|
||||
- cancel_order: cancel a specified order
|
||||
- cancel_all_orders: cancel all orders
|
||||
- pause_grid: pause grid trading (in a trending market)
|
||||
- resume_grid: resume grid trading (in a ranging market)
|
||||
- adjust_grid: adjust the grid boundaries
|
||||
- hold: keep the current state, take no action
|
||||
|
||||
## 输出格式
|
||||
输出JSON数组,每个决策包含:
|
||||
- symbol: 交易对
|
||||
- action: 操作类型
|
||||
- price: 价格(限价单用)
|
||||
- quantity: 数量
|
||||
- level_index: 网格层级索引
|
||||
- order_id: 订单ID(取消订单用)
|
||||
- confidence: 置信度 0-100
|
||||
- reasoning: 决策理由
|
||||
## Output Format
|
||||
Output a JSON array; each decision contains:
|
||||
- symbol: trading pair
|
||||
- action: action type
|
||||
- price: price (for limit orders)
|
||||
- quantity: quantity
|
||||
- level_index: grid level index
|
||||
- order_id: order ID (for canceling orders)
|
||||
- confidence: confidence 0-100
|
||||
- reasoning: decision reasoning
|
||||
|
||||
示例:
|
||||
Example:
|
||||
[
|
||||
{"symbol": "BTCUSDT", "action": "place_buy_limit", "price": 94000, "quantity": 0.01, "level_index": 2, "confidence": 85, "reasoning": "第2层价格接近,下买单"},
|
||||
{"symbol": "BTCUSDT", "action": "hold", "confidence": 90, "reasoning": "市场震荡,保持当前网格"}
|
||||
{"symbol": "BTCUSDT", "action": "place_buy_limit", "price": 94000, "quantity": 0.01, "level_index": 2, "confidence": 85, "reasoning": "Level 2 price is near, place a buy order"},
|
||||
{"symbol": "BTCUSDT", "action": "hold", "confidence": 90, "reasoning": "Market is ranging, keep the current grid"}
|
||||
]
|
||||
`, config.Symbol, config.Symbol, config.GridCount, config.TotalInvestment, config.Leverage, config.Distribution)
|
||||
}
|
||||
@@ -216,26 +216,26 @@ func BuildGridUserPrompt(ctx *GridContext, lang string) string {
|
||||
func buildGridUserPromptZh(ctx *GridContext) string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(fmt.Sprintf("## 当前时间: %s\n\n", ctx.CurrentTime))
|
||||
sb.WriteString(fmt.Sprintf("## Current Time: %s\n\n", ctx.CurrentTime))
|
||||
|
||||
// Market data section
|
||||
sb.WriteString("## 市场数据\n")
|
||||
sb.WriteString(fmt.Sprintf("- 当前价格: $%.2f\n", ctx.CurrentPrice))
|
||||
sb.WriteString(fmt.Sprintf("- 1小时涨跌: %.2f%%\n", ctx.PriceChange1h))
|
||||
sb.WriteString(fmt.Sprintf("- 4小时涨跌: %.2f%%\n", ctx.PriceChange4h))
|
||||
sb.WriteString("## Market Data\n")
|
||||
sb.WriteString(fmt.Sprintf("- Current Price: $%.2f\n", ctx.CurrentPrice))
|
||||
sb.WriteString(fmt.Sprintf("- 1h Change: %.2f%%\n", ctx.PriceChange1h))
|
||||
sb.WriteString(fmt.Sprintf("- 4h Change: %.2f%%\n", ctx.PriceChange4h))
|
||||
sb.WriteString(fmt.Sprintf("- ATR14: $%.2f (%.2f%%)\n", ctx.ATR14, ctx.ATR14/ctx.CurrentPrice*100))
|
||||
sb.WriteString(fmt.Sprintf("- 布林带: 上轨 $%.2f, 中轨 $%.2f, 下轨 $%.2f\n", ctx.BollingerUpper, ctx.BollingerMiddle, ctx.BollingerLower))
|
||||
sb.WriteString(fmt.Sprintf("- 布林带宽度: %.2f%%\n", ctx.BollingerWidth))
|
||||
sb.WriteString(fmt.Sprintf("- EMA20: $%.2f, EMA50: $%.2f, 距离: %.2f%%\n", ctx.EMA20, ctx.EMA50, ctx.EMADistance))
|
||||
sb.WriteString(fmt.Sprintf("- Bollinger Bands: Upper $%.2f, Middle $%.2f, Lower $%.2f\n", ctx.BollingerUpper, ctx.BollingerMiddle, ctx.BollingerLower))
|
||||
sb.WriteString(fmt.Sprintf("- Bollinger Width: %.2f%%\n", ctx.BollingerWidth))
|
||||
sb.WriteString(fmt.Sprintf("- EMA20: $%.2f, EMA50: $%.2f, Distance: %.2f%%\n", ctx.EMA20, ctx.EMA50, ctx.EMADistance))
|
||||
sb.WriteString(fmt.Sprintf("- RSI14: %.1f\n", ctx.RSI14))
|
||||
sb.WriteString(fmt.Sprintf("- MACD: %.4f, Signal: %.4f, Histogram: %.4f\n", ctx.MACD, ctx.MACDSignal, ctx.MACDHistogram))
|
||||
sb.WriteString(fmt.Sprintf("- 资金费率: %.4f%%\n", ctx.FundingRate*100))
|
||||
sb.WriteString(fmt.Sprintf("- Funding Rate: %.4f%%\n", ctx.FundingRate*100))
|
||||
sb.WriteString("\n")
|
||||
|
||||
// Box Indicator Section
|
||||
if ctx.BoxData != nil {
|
||||
sb.WriteString("## 箱体指标 (唐奇安通道)\n\n")
|
||||
sb.WriteString("| 箱体级别 | 上轨 | 下轨 | 宽度 |\n")
|
||||
sb.WriteString("## Box Indicator (Donchian Channel)\n\n")
|
||||
sb.WriteString("| Box Level | Upper | Lower | Width |\n")
|
||||
sb.WriteString("|----------|------|------|------|\n")
|
||||
|
||||
shortWidth := 0.0
|
||||
@@ -248,59 +248,59 @@ func buildGridUserPromptZh(ctx *GridContext) string {
|
||||
longWidth = (ctx.BoxData.LongUpper - ctx.BoxData.LongLower) / ctx.BoxData.CurrentPrice * 100
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("| 短期 (3天) | %.2f | %.2f | %.2f%% |\n",
|
||||
sb.WriteString(fmt.Sprintf("| Short (3d) | %.2f | %.2f | %.2f%% |\n",
|
||||
ctx.BoxData.ShortUpper, ctx.BoxData.ShortLower, shortWidth))
|
||||
sb.WriteString(fmt.Sprintf("| 中期 (10天) | %.2f | %.2f | %.2f%% |\n",
|
||||
sb.WriteString(fmt.Sprintf("| Mid (10d) | %.2f | %.2f | %.2f%% |\n",
|
||||
ctx.BoxData.MidUpper, ctx.BoxData.MidLower, midWidth))
|
||||
sb.WriteString(fmt.Sprintf("| 长期 (21天) | %.2f | %.2f | %.2f%% |\n",
|
||||
sb.WriteString(fmt.Sprintf("| Long (21d) | %.2f | %.2f | %.2f%% |\n",
|
||||
ctx.BoxData.LongUpper, ctx.BoxData.LongLower, longWidth))
|
||||
|
||||
sb.WriteString(fmt.Sprintf("\n当前价格: %.2f\n", ctx.BoxData.CurrentPrice))
|
||||
sb.WriteString(fmt.Sprintf("\nCurrent Price: %.2f\n", ctx.BoxData.CurrentPrice))
|
||||
|
||||
// Check position relative to boxes
|
||||
price := ctx.BoxData.CurrentPrice
|
||||
if price > ctx.BoxData.LongUpper || price < ctx.BoxData.LongLower {
|
||||
sb.WriteString("⚠️ 突破: 价格突破长期箱体!\n")
|
||||
sb.WriteString("⚠️ Breakout: price broke out of the long-term box!\n")
|
||||
} else if price > ctx.BoxData.MidUpper || price < ctx.BoxData.MidLower {
|
||||
sb.WriteString("⚠️ 警告: 价格接近长期箱体边界\n")
|
||||
sb.WriteString("⚠️ Warning: price is approaching the long-term box boundary\n")
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
// Account section
|
||||
sb.WriteString("## 账户状态\n")
|
||||
sb.WriteString(fmt.Sprintf("- 总权益: $%.2f\n", ctx.TotalEquity))
|
||||
sb.WriteString(fmt.Sprintf("- 可用余额: $%.2f\n", ctx.AvailableBalance))
|
||||
sb.WriteString(fmt.Sprintf("- 当前持仓: %.4f (净头寸)\n", ctx.CurrentPosition))
|
||||
sb.WriteString(fmt.Sprintf("- 未实现盈亏: $%.2f\n", ctx.UnrealizedPnL))
|
||||
sb.WriteString("## Account Status\n")
|
||||
sb.WriteString(fmt.Sprintf("- Total Equity: $%.2f\n", ctx.TotalEquity))
|
||||
sb.WriteString(fmt.Sprintf("- Available Balance: $%.2f\n", ctx.AvailableBalance))
|
||||
sb.WriteString(fmt.Sprintf("- Current Position: %.4f (net position)\n", ctx.CurrentPosition))
|
||||
sb.WriteString(fmt.Sprintf("- Unrealized PnL: $%.2f\n", ctx.UnrealizedPnL))
|
||||
sb.WriteString("\n")
|
||||
|
||||
// Grid state section
|
||||
sb.WriteString("## 网格状态\n")
|
||||
sb.WriteString(fmt.Sprintf("- 网格范围: $%.2f - $%.2f\n", ctx.LowerPrice, ctx.UpperPrice))
|
||||
sb.WriteString(fmt.Sprintf("- 网格间距: $%.2f\n", ctx.GridSpacing))
|
||||
sb.WriteString(fmt.Sprintf("- 活跃订单数: %d\n", ctx.ActiveOrderCount))
|
||||
sb.WriteString(fmt.Sprintf("- 已成交层数: %d\n", ctx.FilledLevelCount))
|
||||
sb.WriteString(fmt.Sprintf("- 网格已暂停: %v\n", ctx.IsPaused))
|
||||
sb.WriteString("## Grid State\n")
|
||||
sb.WriteString(fmt.Sprintf("- Grid Range: $%.2f - $%.2f\n", ctx.LowerPrice, ctx.UpperPrice))
|
||||
sb.WriteString(fmt.Sprintf("- Grid Spacing: $%.2f\n", ctx.GridSpacing))
|
||||
sb.WriteString(fmt.Sprintf("- Active Orders: %d\n", ctx.ActiveOrderCount))
|
||||
sb.WriteString(fmt.Sprintf("- Filled Levels: %d\n", ctx.FilledLevelCount))
|
||||
sb.WriteString(fmt.Sprintf("- Grid Paused: %v\n", ctx.IsPaused))
|
||||
if ctx.CurrentDirection != "" {
|
||||
directionDescZh := map[string]string{
|
||||
"neutral": "中性 (50%买+50%卖)",
|
||||
"long": "做多 (100%买)",
|
||||
"short": "做空 (100%卖)",
|
||||
"long_bias": "偏多 (70%买+30%卖)",
|
||||
"short_bias": "偏空 (30%买+70%卖)",
|
||||
"neutral": "Neutral (50% buy + 50% sell)",
|
||||
"long": "Long (100% buy)",
|
||||
"short": "Short (100% sell)",
|
||||
"long_bias": "Long bias (70% buy + 30% sell)",
|
||||
"short_bias": "Short bias (30% buy + 70% sell)",
|
||||
}
|
||||
desc := directionDescZh[ctx.CurrentDirection]
|
||||
if desc == "" {
|
||||
desc = ctx.CurrentDirection
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("- 网格方向: %s\n", desc))
|
||||
sb.WriteString(fmt.Sprintf("- Grid Direction: %s\n", desc))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
// Grid levels detail
|
||||
sb.WriteString("## 网格层级详情\n")
|
||||
sb.WriteString("| 层级 | 价格 | 状态 | 方向 | 订单数量 | 持仓数量 | 未实现盈亏 |\n")
|
||||
sb.WriteString("## Grid Level Details\n")
|
||||
sb.WriteString("| Level | Price | State | Side | Order Qty | Position Qty | Unrealized PnL |\n")
|
||||
sb.WriteString("|------|------|------|------|----------|----------|------------|\n")
|
||||
for _, level := range ctx.Levels {
|
||||
sb.WriteString(fmt.Sprintf("| %d | $%.2f | %s | %s | %.4f | %.4f | $%.2f |\n",
|
||||
@@ -310,16 +310,16 @@ func buildGridUserPromptZh(ctx *GridContext) string {
|
||||
sb.WriteString("\n")
|
||||
|
||||
// Performance section
|
||||
sb.WriteString("## 绩效统计\n")
|
||||
sb.WriteString(fmt.Sprintf("- 总利润: $%.2f\n", ctx.TotalProfit))
|
||||
sb.WriteString(fmt.Sprintf("- 总交易次数: %d\n", ctx.TotalTrades))
|
||||
sb.WriteString(fmt.Sprintf("- 胜率: %.1f%%\n", float64(ctx.WinningTrades)/float64(max(ctx.TotalTrades, 1))*100))
|
||||
sb.WriteString(fmt.Sprintf("- 最大回撤: %.2f%%\n", ctx.MaxDrawdown))
|
||||
sb.WriteString(fmt.Sprintf("- 今日盈亏: $%.2f\n", ctx.DailyPnL))
|
||||
sb.WriteString("## Performance Statistics\n")
|
||||
sb.WriteString(fmt.Sprintf("- Total Profit: $%.2f\n", ctx.TotalProfit))
|
||||
sb.WriteString(fmt.Sprintf("- Total Trades: %d\n", ctx.TotalTrades))
|
||||
sb.WriteString(fmt.Sprintf("- Win Rate: %.1f%%\n", float64(ctx.WinningTrades)/float64(max(ctx.TotalTrades, 1))*100))
|
||||
sb.WriteString(fmt.Sprintf("- Max Drawdown: %.2f%%\n", ctx.MaxDrawdown))
|
||||
sb.WriteString(fmt.Sprintf("- Today's PnL: $%.2f\n", ctx.DailyPnL))
|
||||
sb.WriteString("\n")
|
||||
|
||||
sb.WriteString("## 请分析以上数据,做出网格交易决策\n")
|
||||
sb.WriteString("输出JSON数组格式的决策列表。\n")
|
||||
sb.WriteString("## Analyze the data above and make grid trading decisions\n")
|
||||
sb.WriteString("Output the decision list as a JSON array.\n")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
@@ -541,9 +541,9 @@ func isValidGridAction(action string) bool {
|
||||
"adjust_grid": true,
|
||||
"hold": true,
|
||||
// Also support standard actions for compatibility
|
||||
"open_long": true,
|
||||
"open_short": true,
|
||||
"close_long": true,
|
||||
"open_long": true,
|
||||
"open_short": true,
|
||||
"close_long": true,
|
||||
"close_short": true,
|
||||
}
|
||||
return validActions[action]
|
||||
|
||||
Reference in New Issue
Block a user