mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-09 22:10:57 +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:
@@ -799,14 +799,51 @@ func (e *StrategyEngine) getVergexSignalCoins(limit int, marketType, chain, liqB
|
||||
return candidates, nil
|
||||
}
|
||||
|
||||
items := make([]vergex.SignalRankItem, 0, limit)
|
||||
// Direction-balanced selection: interleave the top bullish and top bearish
|
||||
// signals so the candidate universe carries BOTH long and short ideas every
|
||||
// cycle (instead of filling up with whichever bias ranks highest). This is
|
||||
// what lets the AI actually judge — and trade — both directions.
|
||||
var bullItems, bearItems, otherItems []vergex.SignalRankItem
|
||||
for _, item := range rankedItems {
|
||||
if category != "" && category != "all" && item.Category != category {
|
||||
continue
|
||||
}
|
||||
items = append(items, item)
|
||||
if len(items) >= limit {
|
||||
break
|
||||
switch strings.ToLower(strings.TrimSpace(item.Bias)) {
|
||||
case "bearish", "short", "sell":
|
||||
bearItems = append(bearItems, item)
|
||||
case "bullish", "long", "buy":
|
||||
bullItems = append(bullItems, item)
|
||||
default:
|
||||
otherItems = append(otherItems, item)
|
||||
}
|
||||
}
|
||||
items := make([]vergex.SignalRankItem, 0, limit)
|
||||
bi, ri, oi := 0, 0, 0
|
||||
for len(items) < limit {
|
||||
progressed := false
|
||||
if bi < len(bullItems) {
|
||||
items = append(items, bullItems[bi])
|
||||
bi++
|
||||
progressed = true
|
||||
if len(items) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
if ri < len(bearItems) {
|
||||
items = append(items, bearItems[ri])
|
||||
ri++
|
||||
progressed = true
|
||||
if len(items) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !progressed {
|
||||
if oi < len(otherItems) {
|
||||
items = append(items, otherItems[oi])
|
||||
oi++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(items) == 0 {
|
||||
@@ -840,6 +877,47 @@ func minInt(a, b int) int {
|
||||
return b
|
||||
}
|
||||
|
||||
// DirectionalCandidates returns bullish (long) and bearish (short) candidate
|
||||
// symbols from the most recent Vergex signal ranking, each ordered by upstream
|
||||
// rank (strongest first). Only populated for vergex_signal coin sources, since
|
||||
// that is the only source carrying a per-symbol directional bias.
|
||||
func (e *StrategyEngine) DirectionalCandidates() (bullish []string, bearish []string) {
|
||||
if e == nil || len(e.vergexRankingCache) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
type ranked struct {
|
||||
sym string
|
||||
rank int
|
||||
}
|
||||
rankKey := func(r int) int {
|
||||
if r > 0 {
|
||||
return r
|
||||
}
|
||||
return 1 << 30
|
||||
}
|
||||
var bl, br []ranked
|
||||
for sym, item := range e.vergexRankingCache {
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(item.Bias)) {
|
||||
case "bearish", "short", "sell":
|
||||
br = append(br, ranked{sym, item.Rank})
|
||||
case "bullish", "long", "buy":
|
||||
bl = append(bl, ranked{sym, item.Rank})
|
||||
}
|
||||
}
|
||||
sort.SliceStable(bl, func(i, j int) bool { return rankKey(bl[i].rank) < rankKey(bl[j].rank) })
|
||||
sort.SliceStable(br, func(i, j int) bool { return rankKey(br[i].rank) < rankKey(br[j].rank) })
|
||||
for _, r := range bl {
|
||||
bullish = append(bullish, r.sym)
|
||||
}
|
||||
for _, r := range br {
|
||||
bearish = append(bearish, r.sym)
|
||||
}
|
||||
return bullish, bearish
|
||||
}
|
||||
|
||||
func withDefaultText(value, fallback string) string {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fallback
|
||||
|
||||
36
kernel/engine_directional_test.go
Normal file
36
kernel/engine_directional_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"nofx/provider/vergex"
|
||||
)
|
||||
|
||||
func TestDirectionalCandidates(t *testing.T) {
|
||||
e := &StrategyEngine{
|
||||
vergexRankingCache: map[string]*vergex.SignalRankItem{
|
||||
"xyz:NVDA": {Symbol: "xyz:NVDA", Bias: "bullish", Rank: 2},
|
||||
"xyz:AAPL": {Symbol: "xyz:AAPL", Bias: "bullish", Rank: 1},
|
||||
"BTC": {Symbol: "BTC", Bias: "bearish", Rank: 3},
|
||||
"ETH": {Symbol: "ETH", Bias: "bearish", Rank: 1},
|
||||
"SOL": {Symbol: "SOL", Bias: "neutral", Rank: 1},
|
||||
},
|
||||
}
|
||||
|
||||
bull, bear := e.DirectionalCandidates()
|
||||
|
||||
if len(bull) != 2 || bull[0] != "xyz:AAPL" || bull[1] != "xyz:NVDA" {
|
||||
t.Fatalf("bullish should be rank-ordered [xyz:AAPL xyz:NVDA], got %v", bull)
|
||||
}
|
||||
if len(bear) != 2 || bear[0] != "ETH" || bear[1] != "BTC" {
|
||||
t.Fatalf("bearish should be rank-ordered [ETH BTC], got %v", bear)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectionalCandidatesEmpty(t *testing.T) {
|
||||
e := &StrategyEngine{}
|
||||
bull, bear := e.DirectionalCandidates()
|
||||
if len(bull) != 0 || len(bear) != 0 {
|
||||
t.Fatalf("empty cache should yield no candidates, got %v %v", bull, bear)
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,8 @@ func (e *StrategyEngine) BuildSystemPrompt(accountEquity float64, variant string
|
||||
sb.WriteString(roleDefinition)
|
||||
sb.WriteString("\n\n")
|
||||
} else if zh {
|
||||
sb.WriteString("# 你是一名专业的 Hyperliquid USDC 多资产交易 AI\n\n")
|
||||
sb.WriteString("你的任务是基于提供的市场数据做出交易决策。\n\n")
|
||||
sb.WriteString("# You are a professional Hyperliquid USDC multi-asset trading AI\n\n")
|
||||
sb.WriteString("Your task is to make trading decisions based on the provided market data.\n\n")
|
||||
} else {
|
||||
sb.WriteString("# You are a professional Hyperliquid USDC multi-asset trading AI\n\n")
|
||||
sb.WriteString("Your task is to make trading decisions based on the provided market data.\n\n")
|
||||
@@ -75,11 +75,11 @@ func (e *StrategyEngine) BuildSystemPrompt(accountEquity float64, variant string
|
||||
sb.WriteString(tradingFrequency)
|
||||
sb.WriteString("\n\n")
|
||||
} else if zh {
|
||||
sb.WriteString("# ⏱️ 交易频率提醒\n\n")
|
||||
sb.WriteString("- 优秀交易员: 每日 2-4 单 ≈ 每小时 0.1-0.2 单\n")
|
||||
sb.WriteString("- 每小时 > 2 单 = 过度交易\n")
|
||||
sb.WriteString("- 单笔持仓时长 ≥ 45-90 分钟\n")
|
||||
sb.WriteString("如果你发现自己每个周期都在交易 → 入场标准过低; 如果不到 45 分钟就平仓 → 太冲动。\n\n")
|
||||
sb.WriteString("# ⏱️ Trading Frequency Awareness\n\n")
|
||||
sb.WriteString("- Excellent traders: 2-4 trades/day ≈ 0.1-0.2 trades/hour\n")
|
||||
sb.WriteString("- >2 trades/hour = overtrading\n")
|
||||
sb.WriteString("- Single position hold time ≥ 45-90 minutes\n")
|
||||
sb.WriteString("If you find yourself trading every cycle → standards too low; if closing positions < 45 minutes → too impulsive.\n\n")
|
||||
} else {
|
||||
sb.WriteString("# ⏱️ Trading Frequency Awareness\n\n")
|
||||
sb.WriteString("- Excellent traders: 2-4 trades/day ≈ 0.1-0.2 trades/hour\n")
|
||||
@@ -93,21 +93,21 @@ func (e *StrategyEngine) BuildSystemPrompt(accountEquity float64, variant string
|
||||
if entryStandards != "" {
|
||||
sb.WriteString(entryStandards)
|
||||
if zh {
|
||||
sb.WriteString("\n\n你拥有以下指标数据:\n")
|
||||
sb.WriteString("\n\nYou have the following indicator data:\n")
|
||||
} else {
|
||||
sb.WriteString("\n\nYou have the following indicator data:\n")
|
||||
}
|
||||
e.writeAvailableIndicators(&sb, zh)
|
||||
if zh {
|
||||
sb.WriteString(fmt.Sprintf("\n**置信度 ≥ %d** 才能开仓。\n\n", riskControl.MinConfidence))
|
||||
sb.WriteString(fmt.Sprintf("\n**Confidence ≥ %d** required to open positions.\n\n", riskControl.MinConfidence))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("\n**Confidence ≥ %d** required to open positions.\n\n", riskControl.MinConfidence))
|
||||
}
|
||||
} else if zh {
|
||||
sb.WriteString("# 🎯 入场标准 (严格)\n\n")
|
||||
sb.WriteString("只有当多重信号共振时才开仓。你拥有:\n")
|
||||
sb.WriteString("# 🎯 Entry Standards (Strict)\n\n")
|
||||
sb.WriteString("Only open positions when multiple signals resonate. You have:\n")
|
||||
e.writeAvailableIndicators(&sb, zh)
|
||||
sb.WriteString(fmt.Sprintf("\n请自由使用任何有效的分析方法, 但**置信度 ≥ %d** 才能开仓; 避免低质量行为, 如单一指标、信号矛盾、横盘震荡、平仓后立刻再开等。\n\n", riskControl.MinConfidence))
|
||||
sb.WriteString(fmt.Sprintf("\nFeel free to use any effective analysis method, but **confidence ≥ %d** is required to open positions; avoid low-quality behaviors such as single-indicator entries, contradictory signals, sideways chop, or re-entering immediately after a close.\n\n", riskControl.MinConfidence))
|
||||
} else {
|
||||
sb.WriteString("# 🎯 Entry Standards (Strict)\n\n")
|
||||
sb.WriteString("Only open positions when multiple signals resonate. You have:\n")
|
||||
@@ -121,10 +121,10 @@ func (e *StrategyEngine) BuildSystemPrompt(accountEquity float64, variant string
|
||||
sb.WriteString(decisionProcess)
|
||||
sb.WriteString("\n\n")
|
||||
} else if zh {
|
||||
sb.WriteString("# 📋 决策流程\n\n")
|
||||
sb.WriteString("1. 检查持仓 → 是否需要止盈止损\n")
|
||||
sb.WriteString("2. 扫描候选标的 + 多周期 → 是否有强信号\n")
|
||||
sb.WriteString("3. 先写思维链, 再输出结构化 JSON\n\n")
|
||||
sb.WriteString("# 📋 Decision Process\n\n")
|
||||
sb.WriteString("1. Check positions → take profit / stop loss?\n")
|
||||
sb.WriteString("2. Scan candidates + multi-timeframe → are there strong signals?\n")
|
||||
sb.WriteString("3. Write chain of thought first, then output structured JSON\n\n")
|
||||
} else {
|
||||
sb.WriteString("# 📋 Decision Process\n\n")
|
||||
sb.WriteString("1. Check positions → take profit / stop loss?\n")
|
||||
@@ -153,14 +153,14 @@ func (e *StrategyEngine) BuildSystemPrompt(accountEquity float64, variant string
|
||||
|
||||
if customPrompt != "" {
|
||||
if zh {
|
||||
sb.WriteString("# 📌 个性化交易策略\n\n")
|
||||
sb.WriteString("# 📌 Personalized Trading Strategy\n\n")
|
||||
} else {
|
||||
sb.WriteString("# 📌 Personalized Trading Strategy\n\n")
|
||||
}
|
||||
sb.WriteString(customPrompt)
|
||||
sb.WriteString("\n\n")
|
||||
if zh {
|
||||
sb.WriteString("说明: 上述个性化策略是基础规则的补充, 不能违反基础风控原则。\n")
|
||||
sb.WriteString("Note: the above personalized strategy supplements the basic rules and may not violate the core risk controls.\n")
|
||||
} else {
|
||||
sb.WriteString("Note: the above personalized strategy supplements the basic rules and may not violate the core risk controls.\n")
|
||||
}
|
||||
@@ -191,21 +191,21 @@ func (e *StrategyEngine) buildVergexSystemPrompt(accountEquity float64, variant
|
||||
sb.WriteString("\n\n---\n\n")
|
||||
|
||||
if zh {
|
||||
sb.WriteString("# 你是 NOFX Claw402 自动交易员\n\n")
|
||||
sb.WriteString("你的任务是交易 Claw402.ai/Vergex 本轮榜单返回的 Hyperliquid 可交易标的。只允许交易本轮候选标的和已有持仓,不要自行发明代码或切换到榜单外标的。\n\n")
|
||||
sb.WriteString("# 决策数据优先级\n\n")
|
||||
sb.WriteString("1. Claw402.ai Signal Ranking: 决定本轮候选池、排名、方向和类别。\n")
|
||||
sb.WriteString("2. Claw402.ai Signal Lab: 用于确认趋势、动量、事件或模型信号;这是开仓前的核心确认数据。\n")
|
||||
sb.WriteString("3. Claw402.ai Cost/Liquidation Heatmap: 用于识别清算密集区、成本区、止损位置和止盈目标。\n")
|
||||
sb.WriteString("4. 原始 OHLCV K 线: 用于验证入场时机、趋势结构、波动和风险回报。\n\n")
|
||||
sb.WriteString("# 交易原则\n\n")
|
||||
sb.WriteString("- 先管理已有持仓,再考虑新开仓。\n")
|
||||
sb.WriteString("- 开仓需要 Signal Lab、热力图和 K 线方向大体一致;任一关键数据缺失或互相冲突时,默认等待。\n")
|
||||
sb.WriteString("- 不要把 Claw402 排名当作唯一买入理由;排名只是候选池,开仓必须经过详情数据和 K 线确认。\n")
|
||||
sb.WriteString("- 本轮 Candidate Coins 中的标的都是允许交易的候选;如果某个标的详情缺失,只能降低置信度或等待,不能说它不属于可交易范围。\n")
|
||||
sb.WriteString("- 如果 Signal Lab 或热力图没有出现在该标的的 Vergex Claw402 Signals 里,必须在 reasoning 中说明缺失;如果已经出现,则不能声称该标的缺少该数据。\n")
|
||||
sb.WriteString("- 防止频繁开平仓:非止损或强止盈情况下,开仓后至少持有 45 分钟;小亏小赚的噪音区优先持有到 90 分钟;平仓后同一标的 90 分钟内不重新进场;每小时最多 1 次新开仓。\n")
|
||||
sb.WriteString("- 止损必须放在无效点之外;止盈优先放在热力图阻力/清算区域或满足风险回报的位置。\n\n")
|
||||
sb.WriteString("# You are the NOFX Claw402 auto-trader\n\n")
|
||||
sb.WriteString("Trade only Hyperliquid instruments returned by this cycle's Claw402.ai/Vergex board. You may trade only the current candidate symbols and existing positions; never invent tickers or rotate outside the provided universe.\n\n")
|
||||
sb.WriteString("# Decision Data Priority\n\n")
|
||||
sb.WriteString("1. Claw402.ai Signal Ranking: candidate pool, rank, direction and category.\n")
|
||||
sb.WriteString("2. Claw402.ai Signal Lab: trend, momentum, event/model confirmation; this is the core pre-entry confirmation source.\n")
|
||||
sb.WriteString("3. Claw402.ai Cost/Liquidation Heatmap: crowded liquidation/cost zones, stop placement and target zones.\n")
|
||||
sb.WriteString("4. Raw OHLCV candles: entry timing, trend structure, volatility and risk/reward validation.\n\n")
|
||||
sb.WriteString("# Trading Rules\n\n")
|
||||
sb.WriteString("- Manage existing positions before opening new ones.\n")
|
||||
sb.WriteString("- Open only when Signal Lab, heatmap and raw candles broadly agree; wait when key data is missing or contradictory.\n")
|
||||
sb.WriteString("- Ranking alone is not an entry reason; it only defines the candidate pool.\n")
|
||||
sb.WriteString("- Every symbol in Candidate Coins is part of the allowed trading universe; missing detail can lower confidence or trigger waiting, but does not make the symbol non-tradable.\n")
|
||||
sb.WriteString("- If Signal Lab or heatmap is absent from that symbol's Vergex Claw402 Signals, state it in reasoning; if it is present, never claim the symbol lacks that data.\n")
|
||||
sb.WriteString("- Avoid churn: unless stopping out or taking a strong profit, hold new positions for at least 45 minutes; avoid flat/noise closes until roughly 90 minutes; after closing a symbol, wait 90 minutes before re-entry; open at most 1 new position per hour.\n")
|
||||
sb.WriteString("- Stops must sit beyond invalidation; targets should prefer heatmap resistance/liquidation zones or valid risk/reward levels.\n\n")
|
||||
} else {
|
||||
sb.WriteString("# You are the NOFX Claw402 auto-trader\n\n")
|
||||
sb.WriteString("Trade only Hyperliquid instruments returned by this cycle's Claw402.ai/Vergex board. You may trade only the current candidate symbols and existing positions; never invent tickers or rotate outside the provided universe.\n\n")
|
||||
@@ -256,15 +256,15 @@ func englishOnlyPromptSection(section string) string {
|
||||
|
||||
func writeVergexSchemaPrompt(sb *strings.Builder, zh bool) {
|
||||
if zh {
|
||||
sb.WriteString("# Claw402.ai TradeFi 数据说明\n\n")
|
||||
sb.WriteString("- Equity: 账户总权益,包含浮动盈亏,单位 USDT。\n")
|
||||
sb.WriteString("- Balance: 可用余额,用于判断还能否开新仓,单位 USDT。\n")
|
||||
sb.WriteString("- Margin: 当前保证金使用率,越高风险越大。\n")
|
||||
sb.WriteString("- Position: 当前持仓,包含方向、进场价、杠杆、未实现盈亏、强平价。\n")
|
||||
sb.WriteString("- Claw402 Ranking: 本轮可交易候选池、排名、方向和类别。\n")
|
||||
sb.WriteString("- Signal Lab: Claw402 对单个标的的深度信号,用于确认趋势和质量。\n")
|
||||
sb.WriteString("- Cost/Liquidation Heatmap: 成本区与清算密集区,用于止损、止盈和拥挤风险判断。\n")
|
||||
sb.WriteString("- Raw OHLCV Kline: 原始 K 线,用于确认趋势结构、入场位置和风险回报。\n")
|
||||
sb.WriteString("# Claw402.ai TradeFi Data Guide\n\n")
|
||||
sb.WriteString("- Equity: total account value including unrealized PnL, in USDT.\n")
|
||||
sb.WriteString("- Balance: available balance for new positions, in USDT.\n")
|
||||
sb.WriteString("- Margin: current margin usage; higher means more risk.\n")
|
||||
sb.WriteString("- Position: current holdings with side, entry, leverage, unrealized PnL and liquidation price.\n")
|
||||
sb.WriteString("- Claw402 Ranking: tradable candidate pool, rank, direction and category for this cycle.\n")
|
||||
sb.WriteString("- Signal Lab: per-symbol Claw402 deep signal used to confirm trend and quality.\n")
|
||||
sb.WriteString("- Cost/Liquidation Heatmap: cost and liquidation clusters used for stops, targets and crowding risk.\n")
|
||||
sb.WriteString("- Raw OHLCV Kline: raw candles used for trend structure, entry timing and risk/reward.\n")
|
||||
} else {
|
||||
sb.WriteString("# Claw402.ai TradeFi Data Guide\n\n")
|
||||
sb.WriteString("- Equity: total account value including unrealized PnL, in USDT.\n")
|
||||
@@ -281,22 +281,22 @@ func writeVergexSchemaPrompt(sb *strings.Builder, zh bool) {
|
||||
func writeVergexHardConstraints(sb *strings.Builder, accountEquity float64, riskControl store.RiskControlConfig, tradeFiPositionValueRatio float64, zh bool) {
|
||||
maxPositionValue := accountEquity * tradeFiPositionValueRatio
|
||||
if zh {
|
||||
sb.WriteString("# 风控硬约束\n\n")
|
||||
sb.WriteString("## 后端强制\n")
|
||||
sb.WriteString(fmt.Sprintf("- 最大持仓数: 同时 %d 个 Claw402 候选标的\n", riskControl.MaxPositions))
|
||||
sb.WriteString(fmt.Sprintf("- 单仓最大名义价值: %.0f USDT (= 权益 %.0f × %.1fx)\n", maxPositionValue, accountEquity, tradeFiPositionValueRatio))
|
||||
sb.WriteString(fmt.Sprintf("- 最大保证金占用: ≤%.0f%%\n", riskControl.MaxMarginUsage*100))
|
||||
sb.WriteString(fmt.Sprintf("- 最小下单金额: ≥%.0f USDT\n\n", riskControl.MinPositionSize))
|
||||
sb.WriteString("## AI 建议\n")
|
||||
sb.WriteString(fmt.Sprintf("- 交易杠杆: Claw402 候选标的最高 %dx\n", riskControl.AltcoinMaxLeverage))
|
||||
sb.WriteString(fmt.Sprintf("- 风险回报比: ≥1:%.1f\n", riskControl.MinRiskRewardRatio))
|
||||
sb.WriteString(fmt.Sprintf("- 最小置信度: ≥%d 才能开仓\n\n", riskControl.MinConfidence))
|
||||
sb.WriteString("# 仓位大小\n\n")
|
||||
sb.WriteString("根据置信度和单仓最大名义价值填写 `position_size_usd`:\n")
|
||||
sb.WriteString("- 高置信 (≥85): 使用上限的 80-100%\n")
|
||||
sb.WriteString("- 中置信 (70-84): 使用上限的 50-80%\n")
|
||||
sb.WriteString("- 低置信 (60-69): 使用上限的 30-50%\n")
|
||||
sb.WriteString("- 不要直接把 available_balance 当作 position_size_usd。\n\n")
|
||||
sb.WriteString("# Hard Risk Constraints\n\n")
|
||||
sb.WriteString("## Backend enforced\n")
|
||||
sb.WriteString(fmt.Sprintf("- Max positions: %d Claw402 candidate instruments at the same time\n", riskControl.MaxPositions))
|
||||
sb.WriteString(fmt.Sprintf("- Max notional per position: %.0f USDT (= equity %.0f × %.1fx)\n", maxPositionValue, accountEquity, tradeFiPositionValueRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Max margin usage: ≤%.0f%%\n", riskControl.MaxMarginUsage*100))
|
||||
sb.WriteString(fmt.Sprintf("- Min order size: ≥%.0f USDT\n\n", riskControl.MinPositionSize))
|
||||
sb.WriteString("## AI guided\n")
|
||||
sb.WriteString(fmt.Sprintf("- Leverage: every open position must use exactly %dx\n", riskControl.AltcoinMaxLeverage))
|
||||
sb.WriteString(fmt.Sprintf("- Risk/reward: ≥1:%.1f\n", riskControl.MinRiskRewardRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Min confidence to open: ≥%d\n\n", riskControl.MinConfidence))
|
||||
sb.WriteString("# Position Sizing\n\n")
|
||||
sb.WriteString("For every `open_long` or `open_short`, use the full max notional per position.\n")
|
||||
sb.WriteString("- Do not scale position_size_usd down by confidence.\n")
|
||||
sb.WriteString("- Do not open small probe positions.\n")
|
||||
sb.WriteString("- If the setup is not strong enough for full size, output `wait`.\n")
|
||||
sb.WriteString("- Do not use available_balance directly as position_size_usd.\n\n")
|
||||
} else {
|
||||
sb.WriteString("# Hard Risk Constraints\n\n")
|
||||
sb.WriteString("## Backend enforced\n")
|
||||
@@ -332,15 +332,21 @@ func writeVergexOutputFormat(sb *strings.Builder, accountEquity float64, riskCon
|
||||
|
||||
sb.WriteString("# Output Format (Strictly Follow)\n\n")
|
||||
if zh {
|
||||
sb.WriteString("必须使用 XML 标签 <reasoning> 和 <decision> 分隔简明分析和决策 JSON。\n\n")
|
||||
sb.WriteString("方向必须由数据决定:上涨结构确认时可以 `open_long`,下跌结构确认时可以 `open_short`;不要默认只做多或只做空。\n\n")
|
||||
sb.WriteString("Use XML tags <reasoning> and <decision> to separate concise analysis from the decision JSON.\n\n")
|
||||
sb.WriteString("Direction must be data-driven: use `open_long` for confirmed upside structures and `open_short` for confirmed downside structures; never default to long-only or short-only behavior.\n\n")
|
||||
if !singleSymbol {
|
||||
sb.WriteString("This cycle you MUST include at least one `open_long` (pick the strongest net-inflow / bullish name) AND at least one `open_short` (pick the strongest net-outflow / bearish name); omit a side only if no suitable name exists for it.\n\n")
|
||||
}
|
||||
} else {
|
||||
sb.WriteString("Use XML tags <reasoning> and <decision> to separate concise analysis from the decision JSON.\n\n")
|
||||
sb.WriteString("Direction must be data-driven: use `open_long` for confirmed upside structures and `open_short` for confirmed downside structures; never default to long-only or short-only behavior.\n\n")
|
||||
if !singleSymbol {
|
||||
sb.WriteString("This cycle you MUST include at least one `open_long` (pick the strongest net-inflow / bullish name) AND at least one `open_short` (pick the strongest net-outflow / bearish name); omit a side only if no suitable name exists for it.\n\n")
|
||||
}
|
||||
}
|
||||
sb.WriteString("<reasoning>\n")
|
||||
if zh {
|
||||
sb.WriteString("简明说明: Claw402 排名、Signal Lab、热力图、K 线是否一致;如果缺数据或冲突,说明为什么等待。\n")
|
||||
sb.WriteString("Briefly state whether Claw402 ranking, Signal Lab, heatmap and candles agree; if data is missing or conflicting, explain why you wait.\n")
|
||||
} else {
|
||||
sb.WriteString("Briefly state whether Claw402 ranking, Signal Lab, heatmap and candles agree; if data is missing or conflicting, explain why you wait.\n")
|
||||
}
|
||||
@@ -357,15 +363,15 @@ func writeVergexOutputFormat(sb *strings.Builder, accountEquity float64, riskCon
|
||||
sb.WriteString("</decision>\n\n")
|
||||
|
||||
if zh {
|
||||
sb.WriteString("## 字段要求\n\n")
|
||||
sb.WriteString("## Field Requirements\n\n")
|
||||
sb.WriteString("- `action`: open_long | open_short | close_long | close_short | hold | wait\n")
|
||||
sb.WriteString(fmt.Sprintf("- `confidence`: 0-100,开仓建议 ≥ %d\n", riskControl.MinConfidence))
|
||||
sb.WriteString("- 开仓时必填: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n")
|
||||
sb.WriteString("- 所有数值必须是算好的数字,不能写公式。\n")
|
||||
sb.WriteString(fmt.Sprintf("- `confidence`: 0-100; recommended ≥ %d to open\n", riskControl.MinConfidence))
|
||||
sb.WriteString("- Required when opening: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n")
|
||||
sb.WriteString("- All numeric values must be calculated numbers, not formulas.\n")
|
||||
if singleSymbol {
|
||||
sb.WriteString(fmt.Sprintf("- 本策略只交易 `%s`,JSON 的 symbol 必须完全等于它。\n", exampleSymbol))
|
||||
sb.WriteString(fmt.Sprintf("- This strategy trades only `%s`; JSON symbol must match it exactly.\n", exampleSymbol))
|
||||
} else {
|
||||
sb.WriteString("- JSON 的 symbol 必须完全来自本轮候选标的或已有持仓;`xyz:` 标的保留前缀,core crypto 标的不要添加 `xyz:` 或 `USDT` 后缀。\n")
|
||||
sb.WriteString("- JSON symbols must exactly match current candidates or existing positions; keep `xyz:` on XYZ instruments, and do not add `xyz:` or `USDT` to core crypto symbols.\n")
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
} else {
|
||||
@@ -446,19 +452,19 @@ func writeModeVariant(sb *strings.Builder, variant string, zh bool) {
|
||||
switch strings.ToLower(strings.TrimSpace(variant)) {
|
||||
case "aggressive":
|
||||
if zh {
|
||||
sb.WriteString("## 模式: 激进\n- 优先捕捉趋势突破, 置信度 ≥ 70 时可分批建仓\n- 允许更高仓位, 但必须严格止损并说明风险回报比\n\n")
|
||||
sb.WriteString("## Mode: Aggressive\n- Prioritize capturing trend breakouts; may scale in when confidence ≥ 70\n- Allow larger positions, but must strictly set stop-loss and explain the risk-reward ratio\n\n")
|
||||
} else {
|
||||
sb.WriteString("## Mode: Aggressive\n- Prioritize capturing trend breakouts; may scale in when confidence ≥ 70\n- Allow larger positions, but must strictly set stop-loss and explain the risk-reward ratio\n\n")
|
||||
}
|
||||
case "conservative":
|
||||
if zh {
|
||||
sb.WriteString("## 模式: 保守\n- 只有当多重信号共振时才开仓\n- 优先保本, 连亏后必须暂停多个周期\n\n")
|
||||
sb.WriteString("## Mode: Conservative\n- Open positions only when multiple signals resonate\n- Prioritize capital preservation; pause for multiple periods after consecutive losses\n\n")
|
||||
} else {
|
||||
sb.WriteString("## Mode: Conservative\n- Open positions only when multiple signals resonate\n- Prioritize capital preservation; pause for multiple periods after consecutive losses\n\n")
|
||||
}
|
||||
case "scalping":
|
||||
if zh {
|
||||
sb.WriteString("## 模式: 短线\n- 关注短期动量, 利润目标较小但要求迅速行动\n- 价格两根 K 线内未按预期走 → 立即减仓或止损\n\n")
|
||||
sb.WriteString("## Mode: Scalping\n- Focus on short-term momentum, smaller profit targets but require quick action\n- If price doesn't move as expected within two bars, immediately reduce position or stop-loss\n\n")
|
||||
} else {
|
||||
sb.WriteString("## Mode: Scalping\n- Focus on short-term momentum, smaller profit targets but require quick action\n- If price doesn't move as expected within two bars, immediately reduce position or stop-loss\n\n")
|
||||
}
|
||||
@@ -467,9 +473,9 @@ func writeModeVariant(sb *strings.Builder, variant string, zh bool) {
|
||||
|
||||
func writeHardConstraints(sb *strings.Builder, accountEquity float64, riskControl store.RiskControlConfig, btcEthPosValueRatio, altcoinPosValueRatio float64, singleSymbol bool, primarySymbol string, zh bool) {
|
||||
if zh {
|
||||
sb.WriteString("# 风控硬约束\n\n")
|
||||
sb.WriteString("## 代码强制 (后端校验, 无法绕过):\n")
|
||||
sb.WriteString(fmt.Sprintf("- 最大持仓数: 同时 %d 个标的\n", riskControl.MaxPositions))
|
||||
sb.WriteString("# Hard Constraints (Risk Control)\n\n")
|
||||
sb.WriteString("## CODE ENFORCED (backend validation, cannot be bypassed):\n")
|
||||
sb.WriteString(fmt.Sprintf("- Max Positions: %d instruments simultaneously\n", riskControl.MaxPositions))
|
||||
} else {
|
||||
sb.WriteString("# Hard Constraints (Risk Control)\n\n")
|
||||
sb.WriteString("## CODE ENFORCED (backend validation, cannot be bypassed):\n")
|
||||
@@ -486,14 +492,14 @@ func writeHardConstraints(sb *strings.Builder, accountEquity float64, riskContro
|
||||
maxVal := accountEquity * ratio
|
||||
symLabel := primarySymbol
|
||||
if zh {
|
||||
sb.WriteString(fmt.Sprintf("- 单仓最大价值 (%s): %.0f USDT (= 权益 %.0f × %.1fx)\n", symLabel, maxVal, accountEquity, ratio))
|
||||
sb.WriteString(fmt.Sprintf("- Position Value Limit (%s): max %.0f USDT (= equity %.0f × %.1fx)\n", symLabel, maxVal, accountEquity, ratio))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("- Position Value Limit (%s): max %.0f USDT (= equity %.0f × %.1fx)\n", symLabel, maxVal, accountEquity, ratio))
|
||||
}
|
||||
} else {
|
||||
if zh {
|
||||
sb.WriteString(fmt.Sprintf("- 单仓最大价值 (山寨币/股票): %.0f USDT (= 权益 %.0f × %.1fx)\n", accountEquity*altcoinPosValueRatio, accountEquity, altcoinPosValueRatio))
|
||||
sb.WriteString(fmt.Sprintf("- 单仓最大价值 (BTC/ETH): %.0f USDT (= 权益 %.0f × %.1fx)\n", accountEquity*btcEthPosValueRatio, accountEquity, btcEthPosValueRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Position Value Limit (Altcoin/Stock): max %.0f USDT (= equity %.0f × %.1fx)\n", accountEquity*altcoinPosValueRatio, accountEquity, altcoinPosValueRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Position Value Limit (BTC/ETH): max %.0f USDT (= equity %.0f × %.1fx)\n", accountEquity*btcEthPosValueRatio, accountEquity, btcEthPosValueRatio))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("- Position Value Limit (Altcoin/Stock): max %.0f USDT (= equity %.0f × %.1fx)\n", accountEquity*altcoinPosValueRatio, accountEquity, altcoinPosValueRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Position Value Limit (BTC/ETH): max %.0f USDT (= equity %.0f × %.1fx)\n", accountEquity*btcEthPosValueRatio, accountEquity, btcEthPosValueRatio))
|
||||
@@ -501,9 +507,9 @@ func writeHardConstraints(sb *strings.Builder, accountEquity float64, riskContro
|
||||
}
|
||||
|
||||
if zh {
|
||||
sb.WriteString(fmt.Sprintf("- 最大保证金占用: ≤%.0f%%\n", riskControl.MaxMarginUsage*100))
|
||||
sb.WriteString(fmt.Sprintf("- 最小下单金额: ≥%.0f USDT\n\n", riskControl.MinPositionSize))
|
||||
sb.WriteString("## AI 建议 (推荐遵循):\n")
|
||||
sb.WriteString(fmt.Sprintf("- Max Margin Usage: ≤%.0f%%\n", riskControl.MaxMarginUsage*100))
|
||||
sb.WriteString(fmt.Sprintf("- Min Position Size: ≥%.0f USDT\n\n", riskControl.MinPositionSize))
|
||||
sb.WriteString("## AI GUIDED (recommended):\n")
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("- Max Margin Usage: ≤%.0f%%\n", riskControl.MaxMarginUsage*100))
|
||||
sb.WriteString(fmt.Sprintf("- Min Position Size: ≥%.0f USDT\n\n", riskControl.MinPositionSize))
|
||||
@@ -516,20 +522,20 @@ func writeHardConstraints(sb *strings.Builder, accountEquity float64, riskContro
|
||||
lev = riskControl.BTCETHMaxLeverage
|
||||
}
|
||||
if zh {
|
||||
sb.WriteString(fmt.Sprintf("- 交易杠杆 (%s): 最高 %dx\n", primarySymbol, lev))
|
||||
sb.WriteString(fmt.Sprintf("- Trading Leverage (%s): max %dx\n", primarySymbol, lev))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("- Trading Leverage (%s): max %dx\n", primarySymbol, lev))
|
||||
}
|
||||
} else {
|
||||
if zh {
|
||||
sb.WriteString(fmt.Sprintf("- 交易杠杆: 山寨币/股票 最高 %dx | BTC/ETH 最高 %dx\n", riskControl.AltcoinMaxLeverage, riskControl.BTCETHMaxLeverage))
|
||||
sb.WriteString(fmt.Sprintf("- Trading Leverage: Altcoin/Stock max %dx | BTC/ETH max %dx\n", riskControl.AltcoinMaxLeverage, riskControl.BTCETHMaxLeverage))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("- Trading Leverage: Altcoin/Stock max %dx | BTC/ETH max %dx\n", riskControl.AltcoinMaxLeverage, riskControl.BTCETHMaxLeverage))
|
||||
}
|
||||
}
|
||||
if zh {
|
||||
sb.WriteString(fmt.Sprintf("- 风险回报比: ≥1:%.1f (take_profit / stop_loss)\n", riskControl.MinRiskRewardRatio))
|
||||
sb.WriteString(fmt.Sprintf("- 最小置信度: ≥%d 才开仓\n\n", riskControl.MinConfidence))
|
||||
sb.WriteString(fmt.Sprintf("- Risk-Reward Ratio: ≥1:%.1f (take_profit / stop_loss)\n", riskControl.MinRiskRewardRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Min Confidence: ≥%d to open position\n\n", riskControl.MinConfidence))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("- Risk-Reward Ratio: ≥1:%.1f (take_profit / stop_loss)\n", riskControl.MinRiskRewardRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Min Confidence: ≥%d to open position\n\n", riskControl.MinConfidence))
|
||||
@@ -544,13 +550,13 @@ func writeHardConstraints(sb *strings.Builder, accountEquity float64, riskContro
|
||||
}
|
||||
}
|
||||
if zh {
|
||||
sb.WriteString("## 仓位大小指引\n")
|
||||
sb.WriteString("根据置信度和上面的单仓最大价值算出 `position_size_usd`:\n")
|
||||
sb.WriteString("- 高置信 (≥85): 用最大价值的 80-100%%\n")
|
||||
sb.WriteString("- 中置信 (70-84): 用最大价值的 50-80%%\n")
|
||||
sb.WriteString("- 低置信 (60-69): 用最大价值的 30-50%%\n")
|
||||
sb.WriteString(fmt.Sprintf("- 示例: 权益 %.0f × %.1fx = 最大 %.0f USDT\n", accountEquity, exampleRatio, accountEquity*exampleRatio))
|
||||
sb.WriteString("- **不要**直接拿 available_balance 当 position_size_usd, 用上面的单仓最大价值!\n\n")
|
||||
sb.WriteString("## Position Sizing Guidance\n")
|
||||
sb.WriteString("Calculate `position_size_usd` from your confidence and the Position Value Limits above:\n")
|
||||
sb.WriteString("- High confidence (≥85): use 80-100%% of the position value limit\n")
|
||||
sb.WriteString("- Medium confidence (70-84): use 50-80%% of the position value limit\n")
|
||||
sb.WriteString("- Low confidence (60-69): use 30-50%% of the position value limit\n")
|
||||
sb.WriteString(fmt.Sprintf("- Example: equity %.0f × %.1fx = max %.0f USDT\n", accountEquity, exampleRatio, accountEquity*exampleRatio))
|
||||
sb.WriteString("- **DO NOT** just use available_balance as position_size_usd. Use the Position Value Limit!\n\n")
|
||||
} else {
|
||||
sb.WriteString("## Position Sizing Guidance\n")
|
||||
sb.WriteString("Calculate `position_size_usd` from your confidence and the Position Value Limits above:\n")
|
||||
@@ -566,21 +572,21 @@ func writeOutputFormat(sb *strings.Builder, accountEquity, btcEthPosValueRatio f
|
||||
// Output format schema MUST stay English/structural; parser depends on it.
|
||||
sb.WriteString("# Output Format (Strictly Follow)\n\n")
|
||||
if zh {
|
||||
sb.WriteString("**必须使用 XML 标签 <reasoning> 和 <decision> 分隔思维链和决策 JSON, 避免解析错误**\n\n")
|
||||
sb.WriteString("**Must use XML tags <reasoning> and <decision> to separate chain of thought and decision JSON, avoiding parsing errors**\n\n")
|
||||
} else {
|
||||
sb.WriteString("**Must use XML tags <reasoning> and <decision> to separate chain of thought and decision JSON, avoiding parsing errors**\n\n")
|
||||
}
|
||||
sb.WriteString("## Format Requirements\n\n")
|
||||
sb.WriteString("<reasoning>\n")
|
||||
if zh {
|
||||
sb.WriteString("你的思维链分析...\n- 简明分析你的思考过程\n")
|
||||
sb.WriteString("Your chain of thought analysis...\n- Briefly analyze your thinking process\n")
|
||||
} else {
|
||||
sb.WriteString("Your chain of thought analysis...\n- Briefly analyze your thinking process\n")
|
||||
}
|
||||
sb.WriteString("</reasoning>\n\n")
|
||||
sb.WriteString("<decision>\n")
|
||||
if zh {
|
||||
sb.WriteString("步骤 2: JSON 决策数组\n\n")
|
||||
sb.WriteString("Step 2: JSON decision array\n\n")
|
||||
} else {
|
||||
sb.WriteString("Step 2: JSON decision array\n\n")
|
||||
}
|
||||
@@ -608,13 +614,13 @@ func writeOutputFormat(sb *strings.Builder, accountEquity, btcEthPosValueRatio f
|
||||
sb.WriteString("</decision>\n\n")
|
||||
|
||||
if zh {
|
||||
sb.WriteString("## 字段说明\n\n")
|
||||
sb.WriteString("## Field Description\n\n")
|
||||
sb.WriteString("- `action`: open_long | open_short | close_long | close_short | hold | wait\n")
|
||||
sb.WriteString(fmt.Sprintf("- `confidence`: 0-100 (开仓建议 ≥ %d)\n", riskControl.MinConfidence))
|
||||
sb.WriteString("- 开仓时必填: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n")
|
||||
sb.WriteString("- **重要**: 所有数值必须是算好的数字, 不能是公式/表达式 (例如写 `27.76`, 不要写 `3000 * 0.01`)\n")
|
||||
sb.WriteString(fmt.Sprintf("- `confidence`: 0-100 (opening recommended ≥ %d)\n", riskControl.MinConfidence))
|
||||
sb.WriteString("- Required when opening: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n")
|
||||
sb.WriteString("- **IMPORTANT**: all numeric values must be calculated numbers, NOT formulas/expressions (e.g. use `27.76`, not `3000 * 0.01`)\n")
|
||||
if singleSymbol {
|
||||
sb.WriteString(fmt.Sprintf("- **本策略只交易 %s**, JSON 中的 `symbol` 必须**完全等于** `%s`, 不要写成 `%s` 去掉后缀或加 USDT 的变体。\n", primarySymbol, primarySymbol, primarySymbol))
|
||||
sb.WriteString(fmt.Sprintf("- **This strategy trades only %s.** The JSON `symbol` MUST match `%s` exactly — do not write `%s` variants that drop the suffix or add USDT.\n", primarySymbol, primarySymbol, primarySymbol))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
} else {
|
||||
@@ -642,9 +648,9 @@ func (e *StrategyEngine) writeAvailableIndicators(sb *strings.Builder, zh bool)
|
||||
}
|
||||
|
||||
if zh {
|
||||
sb.WriteString(fmt.Sprintf("- %s 价格序列", kline.PrimaryTimeframe))
|
||||
sb.WriteString(fmt.Sprintf("- %s price series", kline.PrimaryTimeframe))
|
||||
if kline.EnableMultiTimeframe {
|
||||
sb.WriteString(fmt.Sprintf(" + %s K 线序列\n", kline.LongerTimeframe))
|
||||
sb.WriteString(fmt.Sprintf(" + %s K-line series\n", kline.LongerTimeframe))
|
||||
} else {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
@@ -658,50 +664,50 @@ func (e *StrategyEngine) writeAvailableIndicators(sb *strings.Builder, zh bool)
|
||||
}
|
||||
|
||||
if indicators.EnableEMA {
|
||||
sb.WriteString("- " + label("EMA indicators", "EMA 指标"))
|
||||
sb.WriteString("- " + label("EMA indicators", "EMA indicators"))
|
||||
if len(indicators.EMAPeriods) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" (%s: %v)", label("periods", "周期"), indicators.EMAPeriods))
|
||||
sb.WriteString(fmt.Sprintf(" (%s: %v)", label("periods", "periods"), indicators.EMAPeriods))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
if indicators.EnableMACD {
|
||||
sb.WriteString("- " + label("MACD indicators", "MACD 指标") + "\n")
|
||||
sb.WriteString("- " + label("MACD indicators", "MACD indicators") + "\n")
|
||||
}
|
||||
if indicators.EnableRSI {
|
||||
sb.WriteString("- " + label("RSI indicators", "RSI 指标"))
|
||||
sb.WriteString("- " + label("RSI indicators", "RSI indicators"))
|
||||
if len(indicators.RSIPeriods) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" (%s: %v)", label("periods", "周期"), indicators.RSIPeriods))
|
||||
sb.WriteString(fmt.Sprintf(" (%s: %v)", label("periods", "periods"), indicators.RSIPeriods))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
if indicators.EnableATR {
|
||||
sb.WriteString("- " + label("ATR indicators", "ATR 指标"))
|
||||
sb.WriteString("- " + label("ATR indicators", "ATR indicators"))
|
||||
if len(indicators.ATRPeriods) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" (%s: %v)", label("periods", "周期"), indicators.ATRPeriods))
|
||||
sb.WriteString(fmt.Sprintf(" (%s: %v)", label("periods", "periods"), indicators.ATRPeriods))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
if indicators.EnableBOLL {
|
||||
sb.WriteString("- " + label("Bollinger Bands (BOLL) - Upper/Middle/Lower bands", "布林带 (BOLL) - 上/中/下轨"))
|
||||
sb.WriteString("- " + label("Bollinger Bands (BOLL) - Upper/Middle/Lower bands", "Bollinger Bands (BOLL) - Upper/Middle/Lower bands"))
|
||||
if len(indicators.BOLLPeriods) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" (%s: %v)", label("periods", "周期"), indicators.BOLLPeriods))
|
||||
sb.WriteString(fmt.Sprintf(" (%s: %v)", label("periods", "periods"), indicators.BOLLPeriods))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
if indicators.EnableVolume {
|
||||
sb.WriteString("- " + label("Volume data", "成交量数据") + "\n")
|
||||
sb.WriteString("- " + label("Volume data", "Volume data") + "\n")
|
||||
}
|
||||
if indicators.EnableOI {
|
||||
sb.WriteString("- " + label("Open Interest (OI) data", "持仓量 (OI) 数据") + "\n")
|
||||
sb.WriteString("- " + label("Open Interest (OI) data", "Open Interest (OI) data") + "\n")
|
||||
}
|
||||
if indicators.EnableFundingRate {
|
||||
sb.WriteString("- " + label("Funding rate", "资金费率") + "\n")
|
||||
sb.WriteString("- " + label("Funding rate", "Funding rate") + "\n")
|
||||
}
|
||||
if len(e.config.CoinSource.StaticCoins) > 0 || e.config.CoinSource.UseAI500 || e.config.CoinSource.UseOITop {
|
||||
sb.WriteString("- " + label("AI500 / OI_Top filter tags (if available)", "AI500 / OI_Top 过滤标记 (如有)") + "\n")
|
||||
sb.WriteString("- " + label("AI500 / OI_Top filter tags (if available)", "AI500 / OI_Top filter tags (if available)") + "\n")
|
||||
}
|
||||
if indicators.EnableQuantData {
|
||||
sb.WriteString("- " + label("Quantitative data (institutional/retail fund flow, position changes, multi-period price changes)", "量化数据 (机构/散户资金流, 持仓变化, 多周期价格变动)") + "\n")
|
||||
sb.WriteString("- " + label("Quantitative data (institutional/retail fund flow, position changes, multi-period price changes)", "Quantitative data (institutional/retail fund flow, position changes, multi-period price changes)") + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,13 +768,13 @@ func (e *StrategyEngine) BuildUserPrompt(ctx *Context) string {
|
||||
}
|
||||
|
||||
if lang == LangChinese {
|
||||
sb.WriteString("## 历史交易统计\n")
|
||||
sb.WriteString(fmt.Sprintf("总交易: %d 笔 | 盈利因子: %.2f | 夏普比率: %.2f | 盈亏比: %.2f\n",
|
||||
sb.WriteString("## Historical Trading Statistics\n")
|
||||
sb.WriteString(fmt.Sprintf("Total Trades: %d | Profit Factor: %.2f | Sharpe: %.2f | Win/Loss Ratio: %.2f\n",
|
||||
ctx.TradingStats.TotalTrades,
|
||||
ctx.TradingStats.ProfitFactor,
|
||||
ctx.TradingStats.SharpeRatio,
|
||||
winLossRatio))
|
||||
sb.WriteString(fmt.Sprintf("总盈亏: %+.2f USDT | 平均盈利: +%.2f | 平均亏损: -%.2f | 最大回撤: %.1f%%\n",
|
||||
sb.WriteString(fmt.Sprintf("Total PnL: %+.2f USDT | Avg Win: +%.2f | Avg Loss: -%.2f | Max Drawdown: %.1f%%\n",
|
||||
ctx.TradingStats.TotalPnL,
|
||||
ctx.TradingStats.AvgWin,
|
||||
ctx.TradingStats.AvgLoss,
|
||||
@@ -776,13 +782,13 @@ func (e *StrategyEngine) BuildUserPrompt(ctx *Context) string {
|
||||
|
||||
// Performance hints based on profit factor, sharpe, and drawdown
|
||||
if ctx.TradingStats.ProfitFactor >= 1.5 && ctx.TradingStats.SharpeRatio >= 1 {
|
||||
sb.WriteString("表现: 良好 - 保持当前策略\n")
|
||||
sb.WriteString("Performance: GOOD - maintain current strategy\n")
|
||||
} else if ctx.TradingStats.ProfitFactor < 1 {
|
||||
sb.WriteString("表现: 需改进 - 提高盈亏比,优化止盈止损\n")
|
||||
sb.WriteString("Performance: NEEDS IMPROVEMENT - improve win/loss ratio, optimize TP/SL\n")
|
||||
} else if ctx.TradingStats.MaxDrawdownPct > 30 {
|
||||
sb.WriteString("表现: 风险偏高 - 减少仓位,控制回撤\n")
|
||||
sb.WriteString("Performance: HIGH RISK - reduce position size, control drawdown\n")
|
||||
} else {
|
||||
sb.WriteString("表现: 正常 - 有优化空间\n")
|
||||
sb.WriteString("Performance: NORMAL - room for optimization\n")
|
||||
}
|
||||
} else {
|
||||
sb.WriteString("## Historical Trading Statistics\n")
|
||||
|
||||
@@ -11,8 +11,8 @@ func TestBuildSystemPromptUsesVergexClaw402Prompt(t *testing.T) {
|
||||
cfg := store.GetDefaultStrategyConfig("zh")
|
||||
cfg.CoinSource.SourceType = "vergex_signal"
|
||||
cfg.CoinSource.VergexLimit = 5
|
||||
cfg.PromptSections.RoleDefinition = "# 你是一个专业的 Hyperliquid USDC 多资产交易AI"
|
||||
cfg.CustomPrompt = "只做多,不做空。"
|
||||
cfg.PromptSections.RoleDefinition = "# You are a professional Hyperliquid USDC multi-asset trading AI"
|
||||
cfg.CustomPrompt = "Long only, no shorts."
|
||||
|
||||
engine := NewStrategyEngine(&cfg)
|
||||
prompt := engine.BuildSystemPrompt(30, "balanced")
|
||||
@@ -39,9 +39,9 @@ func TestBuildSystemPromptUsesVergexClaw402Prompt(t *testing.T) {
|
||||
t.Fatalf("system prompt must be English-only, got CJK text:\n%s", prompt)
|
||||
}
|
||||
legacyPhrases := []string{
|
||||
"Hyperliquid USDC 多资产交易AI",
|
||||
"只做多",
|
||||
"山寨币",
|
||||
"Hyperliquid USDC multi-asset trading AI",
|
||||
"Long only",
|
||||
"Altcoin",
|
||||
"BTC/ETH",
|
||||
"LONG-ONLY",
|
||||
"Do not short",
|
||||
@@ -61,11 +61,11 @@ func TestBuildSystemPromptFallsBackToEnglishWhenConfiguredLanguageIsChinese(t *t
|
||||
cfg.CoinSource.VergexLimit = 0
|
||||
cfg.CoinSource.VergexMarketType = ""
|
||||
cfg.CoinSource.VergexChain = ""
|
||||
cfg.PromptSections.RoleDefinition = "# 你是中文系统提示词"
|
||||
cfg.PromptSections.TradingFrequency = "# 高频交易\n每分钟交易。"
|
||||
cfg.PromptSections.EntryStandards = "# 入场\n随便开仓。"
|
||||
cfg.PromptSections.DecisionProcess = "# 决策\n直接输出。"
|
||||
cfg.CustomPrompt = "中文偏好不应进入系统提示词。"
|
||||
cfg.PromptSections.RoleDefinition = "# You are a Chinese system prompt"
|
||||
cfg.PromptSections.TradingFrequency = "# High-frequency trading\nTrade every minute."
|
||||
cfg.PromptSections.EntryStandards = "# Entry\nOpen positions freely."
|
||||
cfg.PromptSections.DecisionProcess = "# Decision\nOutput directly."
|
||||
cfg.CustomPrompt = "Chinese preference should not enter the system prompt."
|
||||
|
||||
engine := NewStrategyEngine(&cfg)
|
||||
prompt := engine.BuildSystemPrompt(30, "balanced")
|
||||
|
||||
@@ -105,7 +105,7 @@ func formatContextData(ctx *Context, lang Language) string {
|
||||
|
||||
// formatHeaderZH formats header information (Chinese)
|
||||
func formatHeaderZH(ctx *Context) string {
|
||||
return fmt.Sprintf("# 📊 交易决策请求\n\n时间: %s | 周期: #%d | 运行时长: %d 分钟\n\n",
|
||||
return fmt.Sprintf("# 📊 Trading Decision Request\n\nTime: %s | Cycle: #%d | Runtime: %d minutes\n\n",
|
||||
ctx.CurrentTime, ctx.CallCount, ctx.RuntimeMinutes)
|
||||
}
|
||||
|
||||
@@ -114,18 +114,18 @@ func formatAccountZH(ctx *Context) string {
|
||||
acc := ctx.Account
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("## 账户状态\n\n")
|
||||
sb.WriteString(fmt.Sprintf("总权益: %.2f USDT | ", acc.TotalEquity))
|
||||
sb.WriteString(fmt.Sprintf("可用余额: %.2f USDT (%.1f%%) | ", acc.AvailableBalance, (acc.AvailableBalance/acc.TotalEquity)*100))
|
||||
sb.WriteString(fmt.Sprintf("总盈亏: %+.2f%% | ", acc.TotalPnLPct))
|
||||
sb.WriteString(fmt.Sprintf("保证金使用率: %.1f%% | ", acc.MarginUsedPct))
|
||||
sb.WriteString(fmt.Sprintf("持仓数: %d\n\n", acc.PositionCount))
|
||||
sb.WriteString("## Account Status\n\n")
|
||||
sb.WriteString(fmt.Sprintf("Total Equity: %.2f USDT | ", acc.TotalEquity))
|
||||
sb.WriteString(fmt.Sprintf("Available Balance: %.2f USDT (%.1f%%) | ", acc.AvailableBalance, (acc.AvailableBalance/acc.TotalEquity)*100))
|
||||
sb.WriteString(fmt.Sprintf("Total PnL: %+.2f%% | ", acc.TotalPnLPct))
|
||||
sb.WriteString(fmt.Sprintf("Margin Usage: %.1f%% | ", acc.MarginUsedPct))
|
||||
sb.WriteString(fmt.Sprintf("Position Count: %d\n\n", acc.PositionCount))
|
||||
|
||||
// Add risk warnings
|
||||
if acc.MarginUsedPct > 70 {
|
||||
sb.WriteString("⚠️ **风险警告**: 保证金使用率 > 70%,处于高风险状态!\n\n")
|
||||
sb.WriteString("⚠️ **Risk Warning**: Margin usage > 70%, in a high-risk state!\n\n")
|
||||
} else if acc.MarginUsedPct > 50 {
|
||||
sb.WriteString("⚠️ **风险提示**: 保证金使用率 > 50%,建议谨慎开仓\n\n")
|
||||
sb.WriteString("⚠️ **Risk Notice**: Margin usage > 50%, open positions with caution\n\n")
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
@@ -134,7 +134,7 @@ func formatAccountZH(ctx *Context) string {
|
||||
// formatTradingStatsZH formats historical trading statistics (Chinese)
|
||||
func formatTradingStatsZH(stats *TradingStats) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("## 历史交易统计\n\n")
|
||||
sb.WriteString("## Historical Trading Statistics\n\n")
|
||||
|
||||
// Win/loss ratio calculation
|
||||
var winLossRatio float64
|
||||
@@ -143,49 +143,49 @@ func formatTradingStatsZH(stats *TradingStats) string {
|
||||
}
|
||||
|
||||
// Metric definitions (focusing on core metrics, excluding win rate)
|
||||
sb.WriteString("**指标说明**:\n")
|
||||
sb.WriteString("- 盈利因子: 总盈利 ÷ 总亏损(>1表示盈利,>1.5为良好,>2为优秀)\n")
|
||||
sb.WriteString("- 夏普比率: (平均收益 - 无风险收益) ÷ 收益标准差(>1良好,>2优秀)\n")
|
||||
sb.WriteString("- 盈亏比: 平均盈利 ÷ 平均亏损(>1.5为良好,>2为优秀)\n")
|
||||
sb.WriteString("- 最大回撤: 资金曲线从峰值到谷底的最大跌幅(<20%为低风险)\n\n")
|
||||
sb.WriteString("**Metric Definitions**:\n")
|
||||
sb.WriteString("- Profit Factor: Total Profit ÷ Total Loss (>1 means profitable, >1.5 good, >2 excellent)\n")
|
||||
sb.WriteString("- Sharpe Ratio: (Avg Return - Risk-free Return) ÷ Std Dev of Returns (>1 good, >2 excellent)\n")
|
||||
sb.WriteString("- Win/Loss Ratio: Avg Win ÷ Avg Loss (>1.5 good, >2 excellent)\n")
|
||||
sb.WriteString("- Max Drawdown: Largest decline of the equity curve from peak to trough (<20% is low risk)\n\n")
|
||||
|
||||
// Data values
|
||||
sb.WriteString("**当前数据**:\n")
|
||||
sb.WriteString(fmt.Sprintf("- 总交易: %d 笔\n", stats.TotalTrades))
|
||||
sb.WriteString(fmt.Sprintf("- 盈利因子: %.2f\n", stats.ProfitFactor))
|
||||
sb.WriteString(fmt.Sprintf("- 夏普比率: %.2f\n", stats.SharpeRatio))
|
||||
sb.WriteString(fmt.Sprintf("- 盈亏比: %.2f\n", winLossRatio))
|
||||
sb.WriteString(fmt.Sprintf("- 总盈亏: %+.2f USDT\n", stats.TotalPnL))
|
||||
sb.WriteString(fmt.Sprintf("- 平均盈利: +%.2f USDT\n", stats.AvgWin))
|
||||
sb.WriteString(fmt.Sprintf("- 平均亏损: -%.2f USDT\n", stats.AvgLoss))
|
||||
sb.WriteString(fmt.Sprintf("- 最大回撤: %.1f%%\n\n", stats.MaxDrawdownPct))
|
||||
sb.WriteString("**Current Data**:\n")
|
||||
sb.WriteString(fmt.Sprintf("- Total Trades: %d\n", stats.TotalTrades))
|
||||
sb.WriteString(fmt.Sprintf("- Profit Factor: %.2f\n", stats.ProfitFactor))
|
||||
sb.WriteString(fmt.Sprintf("- Sharpe Ratio: %.2f\n", stats.SharpeRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Win/Loss Ratio: %.2f\n", winLossRatio))
|
||||
sb.WriteString(fmt.Sprintf("- Total PnL: %+.2f USDT\n", stats.TotalPnL))
|
||||
sb.WriteString(fmt.Sprintf("- Avg Win: +%.2f USDT\n", stats.AvgWin))
|
||||
sb.WriteString(fmt.Sprintf("- Avg Loss: -%.2f USDT\n", stats.AvgLoss))
|
||||
sb.WriteString(fmt.Sprintf("- Max Drawdown: %.1f%%\n\n", stats.MaxDrawdownPct))
|
||||
|
||||
// Comprehensive analysis and decision guidance
|
||||
sb.WriteString("**决策参考**:\n")
|
||||
sb.WriteString("**Decision Reference**:\n")
|
||||
|
||||
// Provide specific recommendations based on statistics
|
||||
if stats.TotalTrades < 10 {
|
||||
sb.WriteString("- 样本量较小(<10笔),统计结果参考意义有限\n")
|
||||
sb.WriteString("- Small sample size (<10 trades), statistics have limited reference value\n")
|
||||
}
|
||||
|
||||
if stats.ProfitFactor >= 1.5 && stats.SharpeRatio >= 1 {
|
||||
sb.WriteString("- 📈 表现良好: 可以维持当前策略风格\n")
|
||||
sb.WriteString("- 📈 Good performance: you can keep the current strategy style\n")
|
||||
} else if stats.ProfitFactor >= 1.0 {
|
||||
sb.WriteString("- 📊 表现正常: 策略可行但有优化空间\n")
|
||||
sb.WriteString("- 📊 Normal performance: strategy is viable but has room for optimization\n")
|
||||
}
|
||||
|
||||
if stats.ProfitFactor < 1.0 {
|
||||
sb.WriteString("- ⚠️ 盈利因子<1: 亏损大于盈利,需要提高盈亏比,优化止盈止损\n")
|
||||
sb.WriteString("- ⚠️ Profit Factor <1: losses exceed profits, improve win/loss ratio and optimize stops/targets\n")
|
||||
}
|
||||
|
||||
if winLossRatio > 0 && winLossRatio < 1.5 {
|
||||
sb.WriteString("- ⚠️ 盈亏比偏低: 建议让利润奔跑,提高止盈目标\n")
|
||||
sb.WriteString("- ⚠️ Low win/loss ratio: let profits run and raise take-profit targets\n")
|
||||
}
|
||||
|
||||
if stats.MaxDrawdownPct > 30 {
|
||||
sb.WriteString("- ⚠️ 最大回撤过高: 建议降低仓位大小控制风险\n")
|
||||
sb.WriteString("- ⚠️ Max drawdown too high: reduce position size to control risk\n")
|
||||
} else if stats.MaxDrawdownPct < 10 {
|
||||
sb.WriteString("- ✅ 回撤控制良好: 风险管理有效\n")
|
||||
sb.WriteString("- ✅ Drawdown well controlled: risk management is effective\n")
|
||||
}
|
||||
|
||||
sb.WriteString("\n")
|
||||
@@ -195,16 +195,16 @@ func formatTradingStatsZH(stats *TradingStats) string {
|
||||
// formatRecentTradesZH formats recent trades (Chinese)
|
||||
func formatRecentTradesZH(orders []RecentOrder) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("## 最近完成的交易\n\n")
|
||||
sb.WriteString("## Recently Closed Trades\n\n")
|
||||
|
||||
for i, order := range orders {
|
||||
// Determine profit or loss
|
||||
profitOrLoss := "盈利"
|
||||
profitOrLoss := "Profit"
|
||||
if order.RealizedPnL < 0 {
|
||||
profitOrLoss = "亏损"
|
||||
profitOrLoss = "Loss"
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("%d. %s %s | 进场 %.4f 出场 %.4f | %s: %+.2f USDT (%+.2f%%) | %s → %s (%s)\n",
|
||||
sb.WriteString(fmt.Sprintf("%d. %s %s | Entry %.4f Exit %.4f | %s: %+.2f USDT (%+.2f%%) | %s → %s (%s)\n",
|
||||
i+1,
|
||||
order.Symbol,
|
||||
order.Side,
|
||||
@@ -226,37 +226,37 @@ func formatRecentTradesZH(orders []RecentOrder) string {
|
||||
// formatCurrentPositionsZH formats current positions (Chinese)
|
||||
func formatCurrentPositionsZH(ctx *Context) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("## 当前持仓\n\n")
|
||||
sb.WriteString("## Current Positions\n\n")
|
||||
|
||||
for i, pos := range ctx.Positions {
|
||||
// Calculate drawdown
|
||||
drawdown := pos.UnrealizedPnLPct - pos.PeakPnLPct
|
||||
|
||||
sb.WriteString(fmt.Sprintf("%d. %s %s | ", i+1, pos.Symbol, strings.ToUpper(pos.Side)))
|
||||
sb.WriteString(fmt.Sprintf("进场 %.4f 当前 %.4f | ", pos.EntryPrice, pos.MarkPrice))
|
||||
sb.WriteString(fmt.Sprintf("数量 %.4f | ", pos.Quantity))
|
||||
sb.WriteString(fmt.Sprintf("仓位价值 %.2f USDT | ", pos.Quantity*pos.MarkPrice))
|
||||
sb.WriteString(fmt.Sprintf("盈亏 %+.2f%% | ", pos.UnrealizedPnLPct))
|
||||
sb.WriteString(fmt.Sprintf("盈亏金额 %+.2f USDT | ", pos.UnrealizedPnL))
|
||||
sb.WriteString(fmt.Sprintf("峰值盈亏 %.2f%% | ", pos.PeakPnLPct))
|
||||
sb.WriteString(fmt.Sprintf("杠杆 %dx | ", pos.Leverage))
|
||||
sb.WriteString(fmt.Sprintf("保证金 %.0f USDT | ", pos.MarginUsed))
|
||||
sb.WriteString(fmt.Sprintf("强平价 %.4f\n", pos.LiquidationPrice))
|
||||
sb.WriteString(fmt.Sprintf("Entry %.4f Current %.4f | ", pos.EntryPrice, pos.MarkPrice))
|
||||
sb.WriteString(fmt.Sprintf("Quantity %.4f | ", pos.Quantity))
|
||||
sb.WriteString(fmt.Sprintf("Position Value %.2f USDT | ", pos.Quantity*pos.MarkPrice))
|
||||
sb.WriteString(fmt.Sprintf("PnL %+.2f%% | ", pos.UnrealizedPnLPct))
|
||||
sb.WriteString(fmt.Sprintf("PnL Amount %+.2f USDT | ", pos.UnrealizedPnL))
|
||||
sb.WriteString(fmt.Sprintf("Peak PnL %.2f%% | ", pos.PeakPnLPct))
|
||||
sb.WriteString(fmt.Sprintf("Leverage %dx | ", pos.Leverage))
|
||||
sb.WriteString(fmt.Sprintf("Margin %.0f USDT | ", pos.MarginUsed))
|
||||
sb.WriteString(fmt.Sprintf("Liq Price %.4f\n", pos.LiquidationPrice))
|
||||
|
||||
// Add analysis hints
|
||||
if drawdown < -0.30*pos.PeakPnLPct && pos.PeakPnLPct > 0.02 {
|
||||
sb.WriteString(fmt.Sprintf(" ⚠️ **止盈提示**: 当前盈亏从峰值 %.2f%% 回撤到 %.2f%%,回撤幅度 %.2f%%,建议考虑止盈\n",
|
||||
sb.WriteString(fmt.Sprintf(" ⚠️ **Take-Profit Hint**: Current PnL retraced from peak %.2f%% to %.2f%%, drawdown %.2f%%, consider taking profit\n",
|
||||
pos.PeakPnLPct, pos.UnrealizedPnLPct, (drawdown/pos.PeakPnLPct)*100))
|
||||
}
|
||||
|
||||
if pos.UnrealizedPnLPct < -4.0 {
|
||||
sb.WriteString(" ⚠️ **止损提示**: 亏损接近-5%止损线,建议考虑止损\n")
|
||||
sb.WriteString(" ⚠️ **Stop-Loss Hint**: Loss approaching the -5% stop-loss line, consider stopping out\n")
|
||||
}
|
||||
|
||||
// Show current price (if market data available)
|
||||
if ctx.MarketDataMap != nil {
|
||||
if mdata, ok := ctx.MarketDataMap[pos.Symbol]; ok {
|
||||
sb.WriteString(fmt.Sprintf(" 📈 当前价格: %.4f\n", mdata.CurrentPrice))
|
||||
sb.WriteString(fmt.Sprintf(" 📈 Current Price: %.4f\n", mdata.CurrentPrice))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ func formatCurrentPositionsZH(ctx *Context) string {
|
||||
// formatCandidateCoinsZH formats candidate coins (Chinese)
|
||||
func formatCandidateCoinsZH(ctx *Context) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("## 候选币种\n\n")
|
||||
sb.WriteString("## Candidate Coins\n\n")
|
||||
|
||||
for i, coin := range ctx.CandidateCoins {
|
||||
sb.WriteString(fmt.Sprintf("### %d. %s\n\n", i+1, coin.Symbol))
|
||||
@@ -277,7 +277,7 @@ func formatCandidateCoinsZH(ctx *Context) string {
|
||||
// Current price
|
||||
if ctx.MarketDataMap != nil {
|
||||
if mdata, ok := ctx.MarketDataMap[coin.Symbol]; ok {
|
||||
sb.WriteString(fmt.Sprintf("当前价格: %.4f\n\n", mdata.CurrentPrice))
|
||||
sb.WriteString(fmt.Sprintf("Current Price: %.4f\n\n", mdata.CurrentPrice))
|
||||
|
||||
// Kline data (multi-timeframe)
|
||||
if mdata.TimeframeData != nil {
|
||||
@@ -289,7 +289,7 @@ func formatCandidateCoinsZH(ctx *Context) string {
|
||||
// OI data (if available)
|
||||
if ctx.OITopDataMap != nil {
|
||||
if oiData, ok := ctx.OITopDataMap[coin.Symbol]; ok {
|
||||
sb.WriteString(fmt.Sprintf("**持仓量变化**: OI排名 #%d | 变化 %+.2f%% (%+.2fM USDT) | 价格变化 %+.2f%%\n\n",
|
||||
sb.WriteString(fmt.Sprintf("**OI Change**: OI Rank #%d | Change %+.2f%% (%+.2fM USDT) | Price Change %+.2f%%\n\n",
|
||||
oiData.Rank,
|
||||
oiData.OIDeltaPercent,
|
||||
oiData.OIDeltaValue/1_000_000,
|
||||
@@ -297,17 +297,17 @@ func formatCandidateCoinsZH(ctx *Context) string {
|
||||
))
|
||||
|
||||
// OI interpretation
|
||||
oiChange := "增加"
|
||||
oiChange := "increase"
|
||||
if oiData.OIDeltaPercent < 0 {
|
||||
oiChange = "减少"
|
||||
oiChange = "decrease"
|
||||
}
|
||||
priceChange := "上涨"
|
||||
priceChange := "up"
|
||||
if oiData.PriceDeltaPercent < 0 {
|
||||
priceChange = "下跌"
|
||||
priceChange = "down"
|
||||
}
|
||||
|
||||
interpretation := getOIInterpretationZH(oiChange, priceChange)
|
||||
sb.WriteString(fmt.Sprintf("**市场解读**: %s\n\n", interpretation))
|
||||
sb.WriteString(fmt.Sprintf("**Market Interpretation**: %s\n\n", interpretation))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,9 +321,9 @@ func formatKlineDataZH(symbol string, tfData map[string]*market.TimeframeSeriesD
|
||||
|
||||
for _, tf := range timeframes {
|
||||
if data, ok := tfData[tf]; ok && len(data.Klines) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("#### %s 时间框架 (从旧到新)\n\n", tf))
|
||||
sb.WriteString(fmt.Sprintf("#### %s Timeframe (oldest to newest)\n\n", tf))
|
||||
sb.WriteString("```\n")
|
||||
sb.WriteString("时间(UTC) 开盘 最高 最低 收盘 成交量\n")
|
||||
sb.WriteString("Time(UTC) Open High Low Close Volume\n")
|
||||
|
||||
// Only show the latest 30 klines
|
||||
startIdx := 0
|
||||
@@ -346,7 +346,7 @@ func formatKlineDataZH(symbol string, tfData map[string]*market.TimeframeSeriesD
|
||||
|
||||
// Mark the last kline
|
||||
if len(data.Klines) > 0 {
|
||||
sb.WriteString(" <- 当前\n")
|
||||
sb.WriteString(" <- current\n")
|
||||
}
|
||||
|
||||
sb.WriteString("```\n\n")
|
||||
@@ -356,14 +356,13 @@ func formatKlineDataZH(symbol string, tfData map[string]*market.TimeframeSeriesD
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
|
||||
// getOIInterpretationZH returns OI change interpretation (Chinese)
|
||||
func getOIInterpretationZH(oiChange, priceChange string) string {
|
||||
if oiChange == "增加" && priceChange == "上涨" {
|
||||
if oiChange == "increase" && priceChange == "up" {
|
||||
return OIInterpretation.OIUp_PriceUp.ZH
|
||||
} else if oiChange == "增加" && priceChange == "下跌" {
|
||||
} else if oiChange == "increase" && priceChange == "down" {
|
||||
return OIInterpretation.OIUp_PriceDown.ZH
|
||||
} else if oiChange == "减少" && priceChange == "上涨" {
|
||||
} else if oiChange == "decrease" && priceChange == "up" {
|
||||
return OIInterpretation.OIDown_PriceUp.ZH
|
||||
} else {
|
||||
return OIInterpretation.OIDown_PriceDown.ZH
|
||||
@@ -621,7 +620,6 @@ func formatKlineDataEN(symbol string, tfData map[string]*market.TimeframeSeriesD
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
|
||||
// getOIInterpretationEN returns OI change interpretation (English)
|
||||
func getOIInterpretationEN(oiChange, priceChange string) string {
|
||||
if oiChange == "increase" && priceChange == "up" {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -41,43 +41,43 @@ func (pb *PromptBuilder) BuildUserPrompt(ctx *Context) string {
|
||||
return formattedData + pb.getDecisionRequirementsEN()
|
||||
}
|
||||
|
||||
// ========== Chinese Prompts ==========
|
||||
// ========== Chinese Prompts (translated to English) ==========
|
||||
|
||||
func (pb *PromptBuilder) buildSystemPromptZH() string {
|
||||
return `你是一个专业的量化交易AI助手,负责分析市场数据并做出交易决策。
|
||||
return `You are a professional quantitative trading AI assistant, responsible for analyzing market data and making trading decisions.
|
||||
|
||||
## 你的任务
|
||||
## Your Tasks
|
||||
|
||||
1. **分析账户状态**: 评估当前风险水平、保证金使用率、持仓情况
|
||||
2. **分析当前持仓**: 判断是否需要止盈、止损、加仓或持有
|
||||
3. **分析候选币种**: 评估新的交易机会,结合技术分析和资金流向
|
||||
4. **做出决策**: 输出明确的交易决策,包含详细的推理过程
|
||||
1. **Analyze account status**: Evaluate current risk level, margin usage, and position status
|
||||
2. **Analyze current positions**: Decide whether to take profit, stop loss, add to position, or hold
|
||||
3. **Analyze candidate symbols**: Evaluate new trading opportunities, combining technical analysis and capital flow
|
||||
4. **Make decisions**: Output clear trading decisions with detailed reasoning
|
||||
|
||||
## 决策原则
|
||||
## Decision Principles
|
||||
|
||||
### 风险优先
|
||||
- 保证金使用率不得超过30%
|
||||
- 单个持仓亏损达到-5%必须止损
|
||||
- 优先保护资本,再考虑盈利
|
||||
### Risk First
|
||||
- Margin usage must not exceed 30%
|
||||
- A single position losing -5% must be stopped out
|
||||
- Protect capital first, then consider profit
|
||||
|
||||
### 跟踪止盈
|
||||
- 当持仓盈亏从峰值回撤30%时,考虑部分或全部止盈
|
||||
- 例如:Peak PnL +5%,Current PnL +3.5% → 回撤了30%,应该止盈
|
||||
### Trailing Take-Profit
|
||||
- When position PnL retraces 30% from its peak, consider partial or full take-profit
|
||||
- For example: Peak PnL +5%, Current PnL +3.5% -> retraced 30%, should take profit
|
||||
|
||||
### 顺势交易
|
||||
- 只在多个时间框架趋势一致时进场
|
||||
- 结合持仓量(OI)变化判断资金流向真实性
|
||||
- OI增加+价格上涨 = 强多头趋势
|
||||
- OI减少+价格上涨 = 空头平仓(可能反转)
|
||||
### Trend Following
|
||||
- Enter only when multiple timeframes' trends agree
|
||||
- Use open interest (OI) change to judge the authenticity of capital flow
|
||||
- OI up + price up = strong bullish trend
|
||||
- OI down + price up = short covering (possible reversal)
|
||||
|
||||
### 分批操作
|
||||
- 分批建仓:第一次开仓不超过目标仓位的50%
|
||||
- 分批止盈:盈利3%平33%,盈利5%平50%,盈利8%全平
|
||||
- 只在盈利仓位上加仓,永远不要追亏损
|
||||
### Scaling
|
||||
- Scale in: the first open should not exceed 50% of the target position
|
||||
- Scale out: at +3% profit close 33%, at +5% close 50%, at +8% close all
|
||||
- Only add to profitable positions, never chase losses
|
||||
|
||||
## 输出格式要求
|
||||
## Output Format Requirements
|
||||
|
||||
**必须**使用以下JSON格式输出决策:
|
||||
You **must** output decisions in the following JSON format:
|
||||
|
||||
` + "```json" + `
|
||||
[
|
||||
@@ -89,37 +89,37 @@ func (pb *PromptBuilder) buildSystemPromptZH() string {
|
||||
"stop_loss": 42000,
|
||||
"take_profit": 48000,
|
||||
"confidence": 85,
|
||||
"reasoning": "详细的推理过程,说明为什么做出这个决策"
|
||||
"reasoning": "Detailed reasoning explaining why this decision was made"
|
||||
}
|
||||
]
|
||||
` + "```" + `
|
||||
|
||||
### 字段说明
|
||||
### Field Descriptions
|
||||
|
||||
- **symbol**: 交易对(必需)
|
||||
- **action**: 动作类型(必需)
|
||||
- HOLD: 持有当前仓位
|
||||
- PARTIAL_CLOSE: 部分平仓
|
||||
- FULL_CLOSE: 全部平仓
|
||||
- ADD_POSITION: 在现有仓位上加仓
|
||||
- OPEN_NEW: 开设新仓位
|
||||
- WAIT: 等待,不采取任何行动
|
||||
- **leverage**: 杠杆倍数(开新仓时必需)
|
||||
- **position_size_usd**: 仓位大小(USDT,开新仓时必需)
|
||||
- **stop_loss**: 止损价格(开新仓时建议提供)
|
||||
- **take_profit**: 止盈价格(开新仓时建议提供)
|
||||
- **confidence**: 信心度(0-100)
|
||||
- **reasoning**: 推理过程(必需,必须详细说明决策依据)
|
||||
- **symbol**: trading pair (required)
|
||||
- **action**: action type (required)
|
||||
- HOLD: hold the current position
|
||||
- PARTIAL_CLOSE: partially close the position
|
||||
- FULL_CLOSE: fully close the position
|
||||
- ADD_POSITION: add to an existing position
|
||||
- OPEN_NEW: open a new position
|
||||
- WAIT: wait, take no action
|
||||
- **leverage**: leverage multiple (required when opening a new position)
|
||||
- **position_size_usd**: position size (USDT, required when opening a new position)
|
||||
- **stop_loss**: stop-loss price (recommended when opening a new position)
|
||||
- **take_profit**: take-profit price (recommended when opening a new position)
|
||||
- **confidence**: confidence level (0-100)
|
||||
- **reasoning**: reasoning (required, must explain the decision basis in detail)
|
||||
|
||||
## 重要提醒
|
||||
## Important Reminders
|
||||
|
||||
1. **永远不要**混淆已实现盈亏和未实现盈亏
|
||||
2. **永远记得**考虑杠杆对盈亏的放大作用
|
||||
3. **永远关注**Peak PnL,这是判断止盈的关键指标
|
||||
4. **永远结合**持仓量(OI)变化来判断趋势真实性
|
||||
5. **永远遵守**风险管理规则,保护资本是第一位的
|
||||
1. **Never** confuse realized PnL with unrealized PnL
|
||||
2. **Always remember** to account for leverage amplifying PnL
|
||||
3. **Always watch** Peak PnL, the key metric for take-profit decisions
|
||||
4. **Always combine** open interest (OI) change to judge trend authenticity
|
||||
5. **Always follow** risk management rules; protecting capital comes first
|
||||
|
||||
现在,请仔细分析接下来提供的交易数据,并做出专业的决策。`
|
||||
Now, carefully analyze the trading data provided next and make a professional decision.`
|
||||
}
|
||||
|
||||
func (pb *PromptBuilder) getDecisionRequirementsZH() string {
|
||||
@@ -127,30 +127,30 @@ func (pb *PromptBuilder) getDecisionRequirementsZH() string {
|
||||
|
||||
---
|
||||
|
||||
## 📝 现在请做出决策
|
||||
## 📝 Now Make Your Decision
|
||||
|
||||
### 决策步骤
|
||||
### Decision Steps
|
||||
|
||||
1. **分析账户风险**:
|
||||
- 当前保证金使用率是否在安全范围?
|
||||
- 是否有足够资金开新仓?
|
||||
1. **Analyze account risk**:
|
||||
- Is the current margin usage within a safe range?
|
||||
- Is there enough capital to open new positions?
|
||||
|
||||
2. **分析现有持仓**(如果有):
|
||||
- 是否触发止损条件?
|
||||
- 是否触发跟踪止盈条件?
|
||||
- 是否适合加仓?
|
||||
2. **Analyze existing positions** (if any):
|
||||
- Are stop-loss conditions triggered?
|
||||
- Are trailing take-profit conditions triggered?
|
||||
- Is it suitable to add to the position?
|
||||
|
||||
3. **分析候选币种**(如果有):
|
||||
- 技术形态是否符合进场条件?
|
||||
- 持仓量变化是否支持趋势?
|
||||
- 多个时间框架是否共振?
|
||||
3. **Analyze candidate symbols** (if any):
|
||||
- Does the technical pattern meet entry conditions?
|
||||
- Does the open interest change support the trend?
|
||||
- Do multiple timeframes resonate?
|
||||
|
||||
4. **输出决策**:
|
||||
- 使用规定的JSON格式
|
||||
- 提供详细的推理过程
|
||||
- 给出明确的行动指令
|
||||
4. **Output the decision**:
|
||||
- Use the specified JSON format
|
||||
- Provide detailed reasoning
|
||||
- Give clear action instructions
|
||||
|
||||
### 输出示例
|
||||
### Output Example
|
||||
|
||||
` + "```json" + `
|
||||
[
|
||||
@@ -158,7 +158,7 @@ func (pb *PromptBuilder) getDecisionRequirementsZH() string {
|
||||
"symbol": "PIPPINUSDT",
|
||||
"action": "PARTIAL_CLOSE",
|
||||
"confidence": 85,
|
||||
"reasoning": "当前PnL +2.96%,接近历史峰值+2.99%(回撤仅0.03%)。建议部分平仓锁定利润,因为:1) 持仓时间仅11分钟,已获得3%收益;2) 5分钟K线显示价格接近短期阻力位;3) 成交量开始萎缩,上涨动能减弱。建议平仓50%,剩余仓位设置跟踪止盈在峰值回撤20%处。"
|
||||
"reasoning": "Current PnL +2.96%, close to the all-time peak +2.99% (only 0.03% retracement). Recommend partial close to lock in profit because: 1) holding time is only 11 minutes with 3% gain already; 2) the 5-minute candle shows price near short-term resistance; 3) volume is starting to shrink and upward momentum is weakening. Recommend closing 50%, with the remaining position set to a trailing take-profit at 20% retracement from peak."
|
||||
},
|
||||
{
|
||||
"symbol": "HUSDT",
|
||||
@@ -168,12 +168,12 @@ func (pb *PromptBuilder) getDecisionRequirementsZH() string {
|
||||
"stop_loss": 0.1560,
|
||||
"take_profit": 0.1720,
|
||||
"confidence": 75,
|
||||
"reasoning": "HUSDT在5分钟时间框架突破关键阻力位0.1630,持仓量1小时内增加+1.57M (+0.89%),配合价格上涨+4.92%,符合'OI增加+价格上涨'的强多头模式。15分钟和1小时时间框架均呈现上涨趋势,多周期共振。建议开仓做多,止损设在突破点下方-5%,止盈目标+8%。"
|
||||
"reasoning": "HUSDT broke the key resistance 0.1630 on the 5-minute timeframe, open interest increased +1.57M (+0.89%) within 1 hour, together with a price rise of +4.92%, matching the strong bullish 'OI up + price up' pattern. Both the 15-minute and 1-hour timeframes show an uptrend, multi-period resonance. Recommend opening long, with stop-loss set 5% below the breakout point and take-profit target +8%."
|
||||
}
|
||||
]
|
||||
` + "```" + `
|
||||
|
||||
**请立即输出你的决策(JSON格式)**:`
|
||||
**Output your decision immediately (JSON format)**:`
|
||||
}
|
||||
|
||||
// ========== English Prompts ==========
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestPromptBuilder 测试提示词构建器
|
||||
// TestPromptBuilder tests the prompt builder
|
||||
func TestPromptBuilder(t *testing.T) {
|
||||
t.Run("NewPromptBuilder", func(t *testing.T) {
|
||||
builderZH := NewPromptBuilder(LangChinese)
|
||||
@@ -31,17 +31,17 @@ func TestPromptBuilder(t *testing.T) {
|
||||
t.Fatal("System prompt is empty")
|
||||
}
|
||||
|
||||
// 验证包含关键内容
|
||||
// Verify it contains key content
|
||||
mustContain := []string{
|
||||
"量化交易AI助手",
|
||||
"分析账户状态",
|
||||
"分析当前持仓",
|
||||
"分析候选币种",
|
||||
"做出决策",
|
||||
"风险优先",
|
||||
"跟踪止盈",
|
||||
"顺势交易",
|
||||
"分批操作",
|
||||
"quantitative trading AI assistant",
|
||||
"Analyze account status",
|
||||
"Analyze current positions",
|
||||
"Analyze candidate symbols",
|
||||
"Make decisions",
|
||||
"Risk First",
|
||||
"Trailing Take-Profit",
|
||||
"Trend Following",
|
||||
"Scaling",
|
||||
"JSON",
|
||||
"symbol",
|
||||
"action",
|
||||
@@ -54,7 +54,7 @@ func TestPromptBuilder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 验证包含所有有效的action类型
|
||||
// Verify it contains all valid action types
|
||||
actions := []string{"HOLD", "PARTIAL_CLOSE", "FULL_CLOSE", "ADD_POSITION", "OPEN_NEW", "WAIT"}
|
||||
for _, action := range actions {
|
||||
if !strings.Contains(systemPrompt, action) {
|
||||
@@ -71,7 +71,7 @@ func TestPromptBuilder(t *testing.T) {
|
||||
t.Fatal("System prompt is empty")
|
||||
}
|
||||
|
||||
// 验证包含关键内容
|
||||
// Verify it contains key content
|
||||
mustContain := []string{
|
||||
"quantitative trading AI",
|
||||
"Analyze Account Status",
|
||||
@@ -96,7 +96,7 @@ func TestPromptBuilder(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("BuildUserPrompt", func(t *testing.T) {
|
||||
// 创建测试上下文
|
||||
// Create test context
|
||||
ctx := createTestContext()
|
||||
|
||||
builderZH := NewPromptBuilder(LangChinese)
|
||||
@@ -106,27 +106,27 @@ func TestPromptBuilder(t *testing.T) {
|
||||
t.Fatal("User prompt is empty")
|
||||
}
|
||||
|
||||
// 验证包含数据字典
|
||||
if !strings.Contains(userPromptZH, "数据字典") {
|
||||
// Verify it contains the data dictionary
|
||||
if !strings.Contains(userPromptZH, "Data Dictionary") {
|
||||
t.Error("User prompt should contain data dictionary")
|
||||
}
|
||||
|
||||
// 验证包含账户信息
|
||||
// Verify it contains account information
|
||||
if !strings.Contains(userPromptZH, "3079.40") { // Equity
|
||||
t.Error("User prompt should contain account equity")
|
||||
}
|
||||
|
||||
// 验证包含持仓信息
|
||||
// Verify it contains position information
|
||||
if !strings.Contains(userPromptZH, "PIPPINUSDT") {
|
||||
t.Error("User prompt should contain position symbol")
|
||||
}
|
||||
|
||||
// 验证包含决策要求
|
||||
if !strings.Contains(userPromptZH, "现在请做出决策") {
|
||||
// Verify it contains decision requirements
|
||||
if !strings.Contains(userPromptZH, "Now Make Your Decision") {
|
||||
t.Error("User prompt should contain decision requirements")
|
||||
}
|
||||
|
||||
// 英文版本
|
||||
// English version
|
||||
builderEN := NewPromptBuilder(LangEnglish)
|
||||
userPromptEN := builderEN.BuildUserPrompt(ctx)
|
||||
|
||||
@@ -140,7 +140,7 @@ func TestPromptBuilder(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestValidateDecisionFormat 测试决策格式验证
|
||||
// TestValidateDecisionFormat tests decision format validation
|
||||
func TestValidateDecisionFormat(t *testing.T) {
|
||||
t.Run("ValidDecision", func(t *testing.T) {
|
||||
decisions := []Decision{
|
||||
@@ -152,7 +152,7 @@ func TestValidateDecisionFormat(t *testing.T) {
|
||||
StopLoss: 42000,
|
||||
TakeProfit: 48000,
|
||||
Confidence: 85,
|
||||
Reasoning: "详细的推理过程",
|
||||
Reasoning: "Detailed reasoning",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ func TestValidateDecisionFormat(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// OPEN_NEW需要额外字段
|
||||
// OPEN_NEW requires extra fields
|
||||
if action == "OPEN_NEW" {
|
||||
decisions[0].Leverage = 3
|
||||
decisions[0].PositionSizeUSD = 1000
|
||||
@@ -333,7 +333,7 @@ func TestValidateDecisionFormat(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestFormatDecisionExample 测试决策示例格式化
|
||||
// TestFormatDecisionExample tests decision example formatting
|
||||
func TestFormatDecisionExample(t *testing.T) {
|
||||
t.Run("Chinese", func(t *testing.T) {
|
||||
example := FormatDecisionExample(LangChinese)
|
||||
@@ -342,7 +342,7 @@ func TestFormatDecisionExample(t *testing.T) {
|
||||
t.Fatal("Decision example is empty")
|
||||
}
|
||||
|
||||
// 应该是有效的JSON
|
||||
// Should be valid JSON
|
||||
if !strings.HasPrefix(strings.TrimSpace(example), "[") {
|
||||
t.Error("Example should be a JSON array")
|
||||
}
|
||||
@@ -359,14 +359,14 @@ func TestFormatDecisionExample(t *testing.T) {
|
||||
t.Fatal("Decision example is empty")
|
||||
}
|
||||
|
||||
// 验证是有效的JSON格式
|
||||
// Verify it is valid JSON format
|
||||
if !strings.HasPrefix(strings.TrimSpace(example), "[") {
|
||||
t.Error("Example should be a JSON array")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkBuildSystemPrompt 性能测试
|
||||
// BenchmarkBuildSystemPrompt performance benchmark
|
||||
func BenchmarkBuildSystemPrompt(b *testing.B) {
|
||||
builder := NewPromptBuilder(LangChinese)
|
||||
|
||||
@@ -384,7 +384,7 @@ func BenchmarkBuildSystemPrompt(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkBuildUserPrompt 性能测试
|
||||
// BenchmarkBuildUserPrompt performance benchmark
|
||||
func BenchmarkBuildUserPrompt(b *testing.B) {
|
||||
builder := NewPromptBuilder(LangChinese)
|
||||
ctx := createTestContext()
|
||||
@@ -403,7 +403,7 @@ func BenchmarkBuildUserPrompt(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// createTestContext 创建测试用的交易上下文
|
||||
// createTestContext creates a trading context for tests
|
||||
func createTestContext() *Context {
|
||||
return &Context{
|
||||
CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"),
|
||||
|
||||
190
kernel/schema.go
190
kernel/schema.go
@@ -62,156 +62,156 @@ func (d BilingualFieldDef) GetDesc(lang Language) string {
|
||||
var DataDictionary = map[string]map[string]BilingualFieldDef{
|
||||
"AccountMetrics": {
|
||||
"Equity": {
|
||||
NameZH: "总权益",
|
||||
NameZH: "Total Equity",
|
||||
NameEN: "Total Equity",
|
||||
Unit: "USDT",
|
||||
FormulaZH: "可用余额 + 未实现盈亏",
|
||||
FormulaZH: "Available Balance + Unrealized PnL",
|
||||
FormulaEN: "Available Balance + Unrealized PnL",
|
||||
DescZH: "账户的实际净值,包含所有持仓的浮动盈亏",
|
||||
DescZH: "Actual net value of the account, including unrealized P&L of all positions",
|
||||
DescEN: "Actual account value including all unrealized P&L from positions",
|
||||
},
|
||||
"Balance": {
|
||||
NameZH: "可用余额",
|
||||
NameZH: "Available Balance",
|
||||
NameEN: "Available Balance",
|
||||
Unit: "USDT",
|
||||
FormulaZH: "初始资金 + 已实现盈亏",
|
||||
FormulaZH: "Initial Capital + Realized PnL",
|
||||
FormulaEN: "Initial Capital + Realized PnL",
|
||||
DescZH: "可用于开新仓位的资金,不包括已用保证金",
|
||||
DescZH: "Funds available for opening new positions, excluding used margin",
|
||||
DescEN: "Available funds for opening new positions, excluding used margin",
|
||||
},
|
||||
"PnL": {
|
||||
NameZH: "总盈亏百分比",
|
||||
NameZH: "Total PnL Percentage",
|
||||
NameEN: "Total PnL Percentage",
|
||||
Unit: "%",
|
||||
FormulaZH: "(总权益 - 初始资金) / 初始资金 × 100",
|
||||
FormulaZH: "(Total Equity - Initial Capital) / Initial Capital × 100",
|
||||
FormulaEN: "(Total Equity - Initial Capital) / Initial Capital × 100",
|
||||
DescZH: "自系统启动以来的总收益率,+15.87%表示盈利15.87%",
|
||||
DescZH: "Total return since system startup, +15.87% means 15.87% profit",
|
||||
DescEN: "Total return since inception, +15.87% means 15.87% profit",
|
||||
},
|
||||
"Margin": {
|
||||
NameZH: "保证金使用率",
|
||||
NameZH: "Margin Usage Rate",
|
||||
NameEN: "Margin Usage Rate",
|
||||
Unit: "%",
|
||||
FormulaZH: "已用保证金合计 / 总权益 × 100",
|
||||
FormulaZH: "Total Used Margin / Total Equity × 100",
|
||||
FormulaEN: "Total Used Margin / Total Equity × 100",
|
||||
DescZH: "该值越高,账户风险越大。安全值<30%,危险值>70%",
|
||||
DescZH: "The higher this value, the greater the account risk. Safe <30%, Dangerous >70%",
|
||||
DescEN: "Higher value = higher risk. Safe <30%, Dangerous >70%",
|
||||
},
|
||||
},
|
||||
|
||||
"TradeMetrics": {
|
||||
"Entry": {
|
||||
NameZH: "进场价",
|
||||
NameZH: "Entry Price",
|
||||
NameEN: "Entry Price",
|
||||
Unit: "USDT",
|
||||
DescZH: "开仓时的平均价格",
|
||||
DescZH: "Average price when opening the position",
|
||||
DescEN: "Average price when opening position",
|
||||
},
|
||||
"Exit": {
|
||||
NameZH: "出场价",
|
||||
NameZH: "Exit Price",
|
||||
NameEN: "Exit Price",
|
||||
Unit: "USDT",
|
||||
DescZH: "平仓时的平均价格",
|
||||
DescZH: "Average price when closing the position",
|
||||
DescEN: "Average price when closing position",
|
||||
},
|
||||
"Profit": {
|
||||
NameZH: "已实现盈亏",
|
||||
NameZH: "Realized PnL",
|
||||
NameEN: "Realized PnL",
|
||||
Unit: "USDT",
|
||||
FormulaZH: "(出场价 - 进场价) / 进场价 × 杠杆 × 仓位价值",
|
||||
FormulaZH: "(Exit Price - Entry Price) / Entry Price × Leverage × Position Value",
|
||||
FormulaEN: "(Exit Price - Entry Price) / Entry Price × Leverage × Position Value",
|
||||
DescZH: "已平仓交易的实际盈亏,包含手续费。正值=盈利,负值=亏损",
|
||||
DescZH: "Actual P&L of closed trades, including fees. Positive=profit, Negative=loss",
|
||||
DescEN: "Actual profit/loss of closed trades including fees. Positive=profit, Negative=loss",
|
||||
},
|
||||
"PnL%": {
|
||||
NameZH: "盈亏百分比",
|
||||
NameZH: "PnL Percentage",
|
||||
NameEN: "PnL Percentage",
|
||||
Unit: "%",
|
||||
FormulaZH: "(出场价 - 进场价) / 进场价 × 杠杆 × 100",
|
||||
FormulaZH: "(Exit - Entry) / Entry × Leverage × 100",
|
||||
FormulaEN: "(Exit - Entry) / Entry × Leverage × 100",
|
||||
DescZH: "已平仓交易的收益率,+6.71%表示盈利6.71%",
|
||||
DescZH: "Return on a closed trade, +6.71% means 6.71% profit",
|
||||
DescEN: "Return on closed trade, +6.71% means 6.71% profit",
|
||||
},
|
||||
"HoldDuration": {
|
||||
NameZH: "持仓时长",
|
||||
NameZH: "Holding Duration",
|
||||
NameEN: "Holding Duration",
|
||||
Unit: "minutes",
|
||||
DescZH: "从开仓到平仓的时间。<15分钟=超短线,15分钟-4小时=日内,>4小时=波段",
|
||||
DescZH: "Time from open to close. <15min=scalping, 15min-4h=intraday, >4h=swing",
|
||||
DescEN: "Time from open to close. <15min=scalping, 15min-4h=intraday, >4h=swing",
|
||||
},
|
||||
},
|
||||
|
||||
"PositionMetrics": {
|
||||
"UnrealizedPnL%": {
|
||||
NameZH: "未实现盈亏百分比",
|
||||
NameZH: "Unrealized PnL Percentage",
|
||||
NameEN: "Unrealized PnL Percentage",
|
||||
Unit: "%",
|
||||
FormulaZH: "(当前价 - 进场价) / 进场价 × 杠杆 × 100",
|
||||
FormulaZH: "(Current Price - Entry Price) / Entry Price × Leverage × 100",
|
||||
FormulaEN: "(Current Price - Entry Price) / Entry Price × Leverage × 100",
|
||||
DescZH: "当前持仓的浮动盈亏,未平仓前是浮动的",
|
||||
DescZH: "Floating P&L of the current position, fluctuating until closed",
|
||||
DescEN: "Floating P&L of current position, not realized until closed",
|
||||
},
|
||||
"PeakPnL%": {
|
||||
NameZH: "峰值盈亏百分比",
|
||||
NameZH: "Peak PnL Percentage",
|
||||
NameEN: "Peak PnL Percentage",
|
||||
Unit: "%",
|
||||
DescZH: "该持仓曾经达到的最高未实现盈亏。用于判断是否需要止盈",
|
||||
DescZH: "Highest unrealized P&L this position has reached. Used to decide whether to take profit",
|
||||
DescEN: "Historical max unrealized PnL for this position. Used for take-profit decisions",
|
||||
},
|
||||
"Drawdown": {
|
||||
NameZH: "从峰值回撤",
|
||||
NameZH: "Drawdown from Peak",
|
||||
NameEN: "Drawdown from Peak",
|
||||
Unit: "%",
|
||||
FormulaZH: "当前盈亏% - 峰值盈亏%",
|
||||
FormulaZH: "Current PnL% - Peak PnL%",
|
||||
FormulaEN: "Current PnL% - Peak PnL%",
|
||||
DescZH: "负值表示正在回撤。例如:峰值+5%,当前+3%,回撤=-2%",
|
||||
DescZH: "Negative value means pulling back. E.g., Peak +5%, Current +3%, Drawdown = -2%",
|
||||
DescEN: "Negative = pulling back. E.g., Peak +5%, Current +3%, Drawdown = -2%",
|
||||
},
|
||||
"Leverage": {
|
||||
NameZH: "杠杆倍数",
|
||||
NameZH: "Leverage",
|
||||
NameEN: "Leverage",
|
||||
Unit: "x",
|
||||
DescZH: "3x表示价格变动1%,持仓盈亏变动3%。杠杆越高,风险越大",
|
||||
DescZH: "3x means a 1% price move = 3% position PnL. Higher leverage = higher risk",
|
||||
DescEN: "3x means 1% price move = 3% position PnL. Higher leverage = higher risk",
|
||||
},
|
||||
"Margin": {
|
||||
NameZH: "占用保证金",
|
||||
NameZH: "Margin Used",
|
||||
NameEN: "Margin Used",
|
||||
Unit: "USDT",
|
||||
FormulaZH: "仓位价值 / 杠杆",
|
||||
FormulaZH: "Position Value / Leverage",
|
||||
FormulaEN: "Position Value / Leverage",
|
||||
DescZH: "该仓位锁定的保证金金额",
|
||||
DescZH: "Amount of margin locked for this position",
|
||||
DescEN: "Collateral locked for this position",
|
||||
},
|
||||
"LiqPrice": {
|
||||
NameZH: "强平价格",
|
||||
NameZH: "Liquidation Price",
|
||||
NameEN: "Liquidation Price",
|
||||
Unit: "USDT",
|
||||
DescZH: "价格触及此值时会被强制平仓。0.0000表示无爆仓风险",
|
||||
DescZH: "Position will be force-closed when price reaches this value. 0.0000 = no liquidation risk",
|
||||
DescEN: "Price at which position will be force-closed. 0.0000 = no liquidation risk",
|
||||
},
|
||||
},
|
||||
|
||||
"MarketData": {
|
||||
"Volume": {
|
||||
NameZH: "成交量",
|
||||
NameZH: "Volume",
|
||||
NameEN: "Volume",
|
||||
Unit: "base asset",
|
||||
DescZH: "该时间段的交易量",
|
||||
DescZH: "Trading volume in this period",
|
||||
DescEN: "Trading volume in this period",
|
||||
},
|
||||
"OI": {
|
||||
NameZH: "持仓量",
|
||||
NameZH: "Open Interest",
|
||||
NameEN: "Open Interest",
|
||||
Unit: "USDT",
|
||||
DescZH: "未平仓合约的总价值。持仓量增加=资金流入,减少=资金流出",
|
||||
DescZH: "Total value of open contracts. Increasing OI = capital inflow, decreasing = outflow",
|
||||
DescEN: "Total value of open contracts. Increasing OI = capital inflow, decreasing = outflow",
|
||||
},
|
||||
"OIChange": {
|
||||
NameZH: "持仓量变化",
|
||||
NameZH: "OI Change",
|
||||
NameEN: "OI Change",
|
||||
Unit: "USDT & %",
|
||||
DescZH: "1小时内持仓量的变化。用于判断市场真实资金流向",
|
||||
DescZH: "OI change within 1 hour. Used to judge the real market capital flow direction",
|
||||
DescEN: "OI change in 1 hour. Used to determine real capital flow direction",
|
||||
},
|
||||
},
|
||||
@@ -256,30 +256,30 @@ var TradingRules = struct {
|
||||
RiskManagement: map[string]BilingualRuleDef{
|
||||
"MaxMarginUsage": {
|
||||
Value: 0.30,
|
||||
DescZH: "保证金使用率不得超过30%",
|
||||
DescZH: "Margin usage must not exceed 30%",
|
||||
DescEN: "Margin usage must not exceed 30%",
|
||||
ReasonZH: "保留70%的资金应对极端行情和追加保证金",
|
||||
ReasonZH: "Reserve 70% capital for extreme market conditions and margin calls",
|
||||
ReasonEN: "Reserve 70% capital for extreme market conditions and margin calls",
|
||||
},
|
||||
"MaxPositionLoss": {
|
||||
Value: -0.05,
|
||||
DescZH: "单个持仓亏损达到-5%时必须止损",
|
||||
DescZH: "Must stop-loss when single position loss reaches -5%",
|
||||
DescEN: "Must stop-loss when single position loss reaches -5%",
|
||||
ReasonZH: "避免单笔交易造成过大损失",
|
||||
ReasonZH: "Prevent excessive loss from single trade",
|
||||
ReasonEN: "Prevent excessive loss from single trade",
|
||||
},
|
||||
"MaxDailyLoss": {
|
||||
Value: -0.10,
|
||||
DescZH: "单日亏损达到-10%时停止交易",
|
||||
DescZH: "Stop trading when daily loss reaches -10%",
|
||||
DescEN: "Stop trading when daily loss reaches -10%",
|
||||
ReasonZH: "防止情绪化交易导致连续亏损",
|
||||
ReasonZH: "Prevent emotional trading leading to consecutive losses",
|
||||
ReasonEN: "Prevent emotional trading leading to consecutive losses",
|
||||
},
|
||||
"PositionSizeLimit": {
|
||||
Value: 0.15,
|
||||
DescZH: "单个仓位不得超过总权益的15%",
|
||||
DescZH: "Single position must not exceed 15% of total equity",
|
||||
DescEN: "Single position must not exceed 15% of total equity",
|
||||
ReasonZH: "避免过度集中风险",
|
||||
ReasonZH: "Avoid excessive risk concentration",
|
||||
ReasonEN: "Avoid excessive risk concentration",
|
||||
},
|
||||
},
|
||||
@@ -287,16 +287,16 @@ var TradingRules = struct {
|
||||
EntrySignals: map[string]BilingualRuleDef{
|
||||
"VolumeSpike": {
|
||||
Value: 2.0,
|
||||
DescZH: "成交量是平均值的2倍以上时考虑进场",
|
||||
DescZH: "Consider entry when volume is 2x above average",
|
||||
DescEN: "Consider entry when volume is 2x above average",
|
||||
ReasonZH: "放量突破通常意味着强趋势",
|
||||
ReasonZH: "Volume breakout usually indicates strong trend",
|
||||
ReasonEN: "Volume breakout usually indicates strong trend",
|
||||
},
|
||||
"OIChangeThreshold": {
|
||||
Value: 0.02,
|
||||
DescZH: "持仓量1小时内变化超过2%视为显著变化",
|
||||
DescZH: "OI change >2% in 1 hour is considered significant",
|
||||
DescEN: "OI change >2% in 1 hour is considered significant",
|
||||
ReasonZH: "大额资金进出会导致持仓量显著变化",
|
||||
ReasonZH: "Large capital flows cause significant OI changes",
|
||||
ReasonEN: "Large capital flows cause significant OI changes",
|
||||
},
|
||||
},
|
||||
@@ -304,16 +304,16 @@ var TradingRules = struct {
|
||||
ExitSignals: map[string]BilingualRuleDef{
|
||||
"TrailingStop": {
|
||||
Value: 0.30,
|
||||
DescZH: "当盈亏从峰值回撤30%时平仓止盈",
|
||||
DescZH: "Close position when PnL pulls back 30% from peak",
|
||||
DescEN: "Close position when PnL pulls back 30% from peak",
|
||||
ReasonZH: "锁定大部分利润,避免盈利回吐。例如:峰值+5%,回撤到+3.5%时平仓",
|
||||
ReasonZH: "Lock in most profits, avoid profit giveback. E.g., Peak +5%, close at +3.5%",
|
||||
ReasonEN: "Lock in most profits, avoid profit giveback. E.g., Peak +5%, close at +3.5%",
|
||||
},
|
||||
"StopLoss": {
|
||||
Value: -0.05,
|
||||
DescZH: "硬止损设置在-5%",
|
||||
DescZH: "Hard stop-loss at -5%",
|
||||
DescEN: "Hard stop-loss at -5%",
|
||||
ReasonZH: "严格控制单笔最大损失",
|
||||
ReasonZH: "Strictly control maximum single-trade loss",
|
||||
ReasonEN: "Strictly control maximum single-trade loss",
|
||||
},
|
||||
},
|
||||
@@ -321,9 +321,9 @@ var TradingRules = struct {
|
||||
PositionControl: map[string]BilingualRuleDef{
|
||||
"ScaleIn": {
|
||||
Value: map[string]interface{}{"enabled": true, "max_additions": 2, "price_requirement": 0.01},
|
||||
DescZH: "只在盈利仓位上加仓,最多加2次,价格需比平均成本高1%",
|
||||
DescZH: "Only add to winning positions, max 2 additions, price must be 1% above avg cost",
|
||||
DescEN: "Only add to winning positions, max 2 additions, price must be 1% above avg cost",
|
||||
ReasonZH: "顺势加仓,不追亏损",
|
||||
ReasonZH: "Add to winners, never average down losers",
|
||||
ReasonEN: "Add to winners, never average down losers",
|
||||
},
|
||||
"ScaleOut": {
|
||||
@@ -332,9 +332,9 @@ var TradingRules = struct {
|
||||
{"pnl": 0.05, "close_pct": 0.50},
|
||||
{"pnl": 0.08, "close_pct": 1.00},
|
||||
},
|
||||
DescZH: "分批止盈:盈利3%时平33%,5%时平50%,8%时全平",
|
||||
DescZH: "Scale-out: Close 33% at +3%, 50% at +5%, 100% at +8%",
|
||||
DescEN: "Scale-out: Close 33% at +3%, 50% at +5%, 100% at +8%",
|
||||
ReasonZH: "在保证利润的同时让盈利奔跑",
|
||||
ReasonZH: "Lock profits while letting winners run",
|
||||
ReasonEN: "Lock profits while letting winners run",
|
||||
},
|
||||
},
|
||||
@@ -367,28 +367,28 @@ var OIInterpretation = OIInterpretationType{
|
||||
ZH string
|
||||
EN string
|
||||
}{
|
||||
ZH: "强多头趋势(新多单开仓,资金流入做多)",
|
||||
ZH: "Strong bullish trend (new longs opening, capital flowing into long positions)",
|
||||
EN: "Strong bullish trend (new longs opening, capital flowing into long positions)",
|
||||
},
|
||||
OIUp_PriceDown: struct {
|
||||
ZH string
|
||||
EN string
|
||||
}{
|
||||
ZH: "强空头趋势(新空单开仓,资金流入做空)",
|
||||
ZH: "Strong bearish trend (new shorts opening, capital flowing into short positions)",
|
||||
EN: "Strong bearish trend (new shorts opening, capital flowing into short positions)",
|
||||
},
|
||||
OIDown_PriceUp: struct {
|
||||
ZH string
|
||||
EN string
|
||||
}{
|
||||
ZH: "空头平仓(空头止损离场,可能出现反转)",
|
||||
ZH: "Shorts covering (shorts stopped out, potential reversal)",
|
||||
EN: "Shorts covering (shorts stopped out, potential reversal)",
|
||||
},
|
||||
OIDown_PriceDown: struct {
|
||||
ZH string
|
||||
EN string
|
||||
}{
|
||||
ZH: "多头平仓(多头止损离场,可能出现反转)",
|
||||
ZH: "Longs closing (longs stopped out, potential reversal)",
|
||||
EN: "Longs closing (longs stopped out, potential reversal)",
|
||||
},
|
||||
}
|
||||
@@ -407,35 +407,35 @@ type CommonMistake struct {
|
||||
|
||||
var CommonMistakes = []CommonMistake{
|
||||
{
|
||||
ErrorZH: "混淆已实现盈亏和未实现盈亏",
|
||||
ErrorZH: "Confusing realized and unrealized P&L",
|
||||
ErrorEN: "Confusing realized and unrealized P&L",
|
||||
ExampleZH: "将历史交易的盈亏与当前持仓的盈亏相加",
|
||||
ExampleZH: "Adding historical trade P&L with current position P&L",
|
||||
ExampleEN: "Adding historical trade P&L with current position P&L",
|
||||
CorrectZH: "已实现盈亏已经计入账户余额,不应重复计算",
|
||||
CorrectZH: "Realized P&L is already included in account balance, don't double count",
|
||||
CorrectEN: "Realized P&L is already included in account balance, don't double count",
|
||||
},
|
||||
{
|
||||
ErrorZH: "忽略杠杆对盈亏的影响",
|
||||
ErrorZH: "Ignoring leverage's impact on P&L",
|
||||
ErrorEN: "Ignoring leverage's impact on P&L",
|
||||
ExampleZH: "价格涨1%,认为盈利1%",
|
||||
ExampleZH: "Price up 1%, thinking profit is 1%",
|
||||
ExampleEN: "Price up 1%, thinking profit is 1%",
|
||||
CorrectZH: "3x杠杆时,价格涨1%,实际盈利约3%",
|
||||
CorrectZH: "With 3x leverage, 1% price move = ~3% P&L",
|
||||
CorrectEN: "With 3x leverage, 1% price move = ~3% P&L",
|
||||
},
|
||||
{
|
||||
ErrorZH: "不理解Peak PnL的重要性",
|
||||
ErrorZH: "Not understanding Peak PnL's importance",
|
||||
ErrorEN: "Not understanding Peak PnL's importance",
|
||||
ExampleZH: "只关注当前PnL,不关注回撤",
|
||||
ExampleZH: "Only watching current PnL, ignoring drawdown",
|
||||
ExampleEN: "Only watching current PnL, ignoring drawdown",
|
||||
CorrectZH: "当前PnL接近Peak PnL时,应考虑止盈以锁定利润",
|
||||
CorrectZH: "When current PnL near Peak PnL, consider taking profit to lock in gains",
|
||||
CorrectEN: "When current PnL near Peak PnL, consider taking profit to lock in gains",
|
||||
},
|
||||
{
|
||||
ErrorZH: "忽略持仓量(OI)变化",
|
||||
ErrorZH: "Ignoring Open Interest changes",
|
||||
ErrorEN: "Ignoring Open Interest changes",
|
||||
ExampleZH: "只看价格K线,不看资金流向",
|
||||
ExampleZH: "Only watching price candles, not capital flows",
|
||||
ExampleEN: "Only watching price candles, not capital flows",
|
||||
CorrectZH: "结合OI变化判断趋势的真实性和持续性",
|
||||
CorrectZH: "Use OI changes to validate trend authenticity and sustainability",
|
||||
CorrectEN: "Use OI changes to validate trend authenticity and sustainability",
|
||||
},
|
||||
}
|
||||
@@ -452,39 +452,39 @@ func GetSchemaPrompt(lang Language) string {
|
||||
|
||||
// getSchemaPromptZH generates the Chinese prompt
|
||||
func getSchemaPromptZH() string {
|
||||
prompt := "# 📖 数据字典与交易规则\n\n"
|
||||
prompt += "## 📊 字段含义说明\n\n"
|
||||
prompt := "# 📖 Data Dictionary & Trading Rules\n\n"
|
||||
prompt += "## 📊 Field Definitions\n\n"
|
||||
|
||||
// Account metrics
|
||||
prompt += "### 账户指标\n"
|
||||
prompt += "### Account Metrics\n"
|
||||
for key, field := range DataDictionary["AccountMetrics"] {
|
||||
prompt += formatFieldDefZH(key, field)
|
||||
}
|
||||
|
||||
// Trade metrics
|
||||
prompt += "\n### 交易指标\n"
|
||||
prompt += "\n### Trade Metrics\n"
|
||||
for key, field := range DataDictionary["TradeMetrics"] {
|
||||
prompt += formatFieldDefZH(key, field)
|
||||
}
|
||||
|
||||
// Position metrics
|
||||
prompt += "\n### 持仓指标\n"
|
||||
prompt += "\n### Position Metrics\n"
|
||||
for key, field := range DataDictionary["PositionMetrics"] {
|
||||
prompt += formatFieldDefZH(key, field)
|
||||
}
|
||||
|
||||
// Market data
|
||||
prompt += "\n### 市场数据\n"
|
||||
prompt += "\n### Market Data\n"
|
||||
for key, field := range DataDictionary["MarketData"] {
|
||||
prompt += formatFieldDefZH(key, field)
|
||||
}
|
||||
|
||||
// OI interpretation
|
||||
prompt += "\n## 💹 持仓量(OI)变化解读\n\n"
|
||||
prompt += "- **OI增加 + 价格上涨**: " + OIInterpretation.OIUp_PriceUp.ZH + "\n"
|
||||
prompt += "- **OI增加 + 价格下跌**: " + OIInterpretation.OIUp_PriceDown.ZH + "\n"
|
||||
prompt += "- **OI减少 + 价格上涨**: " + OIInterpretation.OIDown_PriceUp.ZH + "\n"
|
||||
prompt += "- **OI减少 + 价格下跌**: " + OIInterpretation.OIDown_PriceDown.ZH + "\n"
|
||||
prompt += "\n## 💹 Open Interest (OI) Change Interpretation\n\n"
|
||||
prompt += "- **OI Up + Price Up**: " + OIInterpretation.OIUp_PriceUp.ZH + "\n"
|
||||
prompt += "- **OI Up + Price Down**: " + OIInterpretation.OIUp_PriceDown.ZH + "\n"
|
||||
prompt += "- **OI Down + Price Up**: " + OIInterpretation.OIDown_PriceUp.ZH + "\n"
|
||||
prompt += "- **OI Down + Price Down**: " + OIInterpretation.OIDown_PriceDown.ZH + "\n"
|
||||
|
||||
return prompt
|
||||
}
|
||||
@@ -530,12 +530,12 @@ func getSchemaPromptEN() string {
|
||||
|
||||
// formatFieldDefZH formats a field definition in Chinese
|
||||
func formatFieldDefZH(key string, field BilingualFieldDef) string {
|
||||
result := "- **" + key + "**(" + field.NameZH + "): " + field.DescZH
|
||||
result := "- **" + key + "** (" + field.NameZH + "): " + field.DescZH
|
||||
if field.FormulaZH != "" {
|
||||
result += " | 公式: `" + field.FormulaZH + "`"
|
||||
result += " | Formula: `" + field.FormulaZH + "`"
|
||||
}
|
||||
if field.Unit != "" {
|
||||
result += " | 单位: " + field.Unit
|
||||
result += " | Unit: " + field.Unit
|
||||
}
|
||||
result += "\n"
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user