feat: simplify Claw402 autopilot trading flow

This commit is contained in:
tinkle-community
2026-06-27 00:37:59 +08:00
parent 961e016d33
commit 24f6421a73
151 changed files with 7453 additions and 41117 deletions

View File

@@ -46,6 +46,9 @@ func (c *StrategyConfig) ClampLimits() {
if c.CoinSource.OILowLimit > MaxCandidateCoins {
c.CoinSource.OILowLimit = MaxCandidateCoins
}
if c.CoinSource.VergexLimit > MaxCandidateCoins {
c.CoinSource.VergexLimit = MaxCandidateCoins
}
// Clamp static coins
if len(c.CoinSource.StaticCoins) > MaxCandidateCoins {
@@ -136,6 +139,8 @@ func (c *StrategyConfig) ClampLimits() {
// must use the exact frontend/backend enum values.
func (c *StrategyConfig) NormalizeProductSchema() {
c.StrategyType = normalizeStrategyType(c.StrategyType)
c.CoinSource.StaticCoins = normalizeSymbols(c.CoinSource.StaticCoins)
c.CoinSource.ExcludedCoins = normalizeSymbols(c.CoinSource.ExcludedCoins)
c.CoinSource.SourceType = normalizeCoinSourceType(c.CoinSource.SourceType)
if c.CoinSource.SourceType == "" {
c.CoinSource.SourceType = inferCoinSourceType(c.CoinSource)
@@ -205,26 +210,53 @@ func (c *StrategyConfig) NormalizeProductSchema() {
if c.CoinSource.HyperRankLimit <= 0 {
c.CoinSource.HyperRankLimit = 5
}
default:
c.CoinSource.SourceType = "hyper_rank"
case "vergex_signal":
c.CoinSource.UseAI500 = false
c.CoinSource.UseOITop = false
c.CoinSource.UseOILow = false
c.CoinSource.UseHyperAll = false
c.CoinSource.UseHyperMain = false
if c.CoinSource.HyperRankCategory == "" {
c.CoinSource.HyperRankCategory = "stock"
minLimit := 10
if len(c.CoinSource.StaticCoins) > 0 {
minLimit = len(c.CoinSource.StaticCoins)
if minLimit > MaxCandidateCoins {
minLimit = MaxCandidateCoins
}
}
if c.CoinSource.HyperRankDirection == "" {
c.CoinSource.HyperRankDirection = "gainers"
if c.CoinSource.VergexLimit < minLimit {
c.CoinSource.VergexLimit = minLimit
}
if c.CoinSource.HyperRankLimit <= 0 {
c.CoinSource.HyperRankLimit = 5
if c.CoinSource.VergexMarketType == "" {
c.CoinSource.VergexMarketType = "all"
}
if c.CoinSource.VergexChain == "" {
c.CoinSource.VergexChain = "hyperliquid"
}
default:
c.CoinSource.SourceType = "vergex_signal"
c.CoinSource.UseAI500 = false
c.CoinSource.UseOITop = false
c.CoinSource.UseOILow = false
c.CoinSource.UseHyperAll = false
c.CoinSource.UseHyperMain = false
minLimit := 10
if len(c.CoinSource.StaticCoins) > 0 {
minLimit = len(c.CoinSource.StaticCoins)
if minLimit > MaxCandidateCoins {
minLimit = MaxCandidateCoins
}
}
if c.CoinSource.VergexLimit < minLimit {
c.CoinSource.VergexLimit = minLimit
}
if c.CoinSource.VergexMarketType == "" {
c.CoinSource.VergexMarketType = "all"
}
if c.CoinSource.VergexChain == "" {
c.CoinSource.VergexChain = "hyperliquid"
}
}
c.CoinSource.StaticCoins = normalizeSymbols(c.CoinSource.StaticCoins)
c.CoinSource.ExcludedCoins = normalizeSymbols(c.CoinSource.ExcludedCoins)
c.Indicators.Klines.PrimaryTimeframe = normalizeTimeframe(c.Indicators.Klines.PrimaryTimeframe)
c.Indicators.Klines.LongerTimeframe = normalizeTimeframe(c.Indicators.Klines.LongerTimeframe)
c.Indicators.Klines.SelectedTimeframes = normalizeTimeframes(c.Indicators.Klines.SelectedTimeframes)
@@ -257,8 +289,10 @@ func normalizeCoinSourceType(value string) string {
return "oi_top"
case strings.Contains(compact, "oilow") || strings.Contains(value, "oi low") || strings.Contains(value, "持仓量最低") || strings.Contains(value, "持仓量较低"):
return "oi_low"
case strings.Contains(compact, "hyperrank") || strings.Contains(compact, "dynamicranking") || strings.Contains(value, "动态榜单") || strings.Contains(value, "涨幅榜"):
case strings.Contains(compact, "hyperrank"):
return "hyper_rank"
case strings.Contains(compact, "vergex") || strings.Contains(compact, "claw402") || strings.Contains(compact, "dynamicranking") || strings.Contains(value, "动态榜单") || strings.Contains(value, "涨幅榜") || strings.Contains(value, "信号榜"):
return "vergex_signal"
case strings.Contains(compact, "hyperall"):
return "hyper_all"
case strings.Contains(compact, "hypermain"):
@@ -284,10 +318,12 @@ func inferCoinSourceType(source CoinSourceConfig) string {
return "hyper_all"
case source.UseHyperMain:
return "hyper_main"
case source.VergexLimit > 0 || source.VergexMarketType != "" || source.VergexChain != "" || source.VergexLiqBand != "":
return "vergex_signal"
case source.HyperRankCategory != "" || source.HyperRankDirection != "" || source.HyperRankLimit > 0:
return "hyper_rank"
default:
return "hyper_rank"
return "vergex_signal"
}
}
@@ -783,6 +819,14 @@ type CoinSourceConfig struct {
HyperRankDirection string `json:"hyper_rank_direction,omitempty"`
// Hyperliquid dynamic ranking maximum count. Defaults to 5 and is hard capped at 10 for AI context safety.
HyperRankLimit int `json:"hyper_rank_limit,omitempty"`
// Vergex signal-ranking maximum count. Defaults to 5 and is hard capped at 10.
VergexLimit int `json:"vergex_limit,omitempty"`
// Vergex market type for detail endpoints, e.g. hip3_perp for Hyperliquid TradeFi perps.
VergexMarketType string `json:"vergex_market_type,omitempty"`
// Vergex chain query parameter. Defaults to hyperliquid.
VergexChain string `json:"vergex_chain,omitempty"`
// Vergex liquidation band query parameter.
VergexLiqBand string `json:"vergex_liq_band,omitempty"`
// Note: API URLs are now built automatically using NofxOSAPIKey from IndicatorConfig
}
@@ -916,28 +960,29 @@ func GetDefaultStrategyConfig(lang string) StrategyConfig {
config := StrategyConfig{
Language: normalizedLang,
CoinSource: CoinSourceConfig{
SourceType: "hyper_rank",
UseAI500: false,
AI500Limit: 3,
UseOITop: false,
OITopLimit: 3,
UseOILow: false,
OILowLimit: 3,
UseHyperAll: false,
UseHyperMain: false,
HyperMainLimit: 30,
HyperRankCategory: "stock",
HyperRankDirection: "gainers",
HyperRankLimit: 5,
SourceType: "vergex_signal",
UseAI500: false,
AI500Limit: 3,
UseOITop: false,
OITopLimit: 3,
UseOILow: false,
OILowLimit: 3,
UseHyperAll: false,
UseHyperMain: false,
HyperMainLimit: 30,
HyperRankCategory: "all",
VergexLimit: 10,
VergexMarketType: "all",
VergexChain: "hyperliquid",
},
Indicators: IndicatorConfig{
Klines: KlineConfig{
PrimaryTimeframe: "5m",
PrimaryCount: 20,
LongerTimeframe: "4h",
LongerCount: 10,
EnableMultiTimeframe: true,
SelectedTimeframes: []string{"5m", "15m", "1h"},
PrimaryTimeframe: "15m",
PrimaryCount: 30,
LongerTimeframe: "",
LongerCount: 0,
EnableMultiTimeframe: false,
SelectedTimeframes: []string{"15m"},
},
EnableRawKlines: true, // Required - raw OHLCV data for AI analysis
EnableEMA: false,
@@ -945,9 +990,9 @@ func GetDefaultStrategyConfig(lang string) StrategyConfig {
EnableRSI: false,
EnableATR: false,
EnableBOLL: false,
EnableVolume: true,
EnableOI: true,
EnableFundingRate: true,
EnableVolume: false,
EnableOI: false,
EnableFundingRate: false,
EMAPeriods: []int{20, 50},
RSIPeriods: []int{7, 14},
ATRPeriods: []int{14},
@@ -969,57 +1014,57 @@ func GetDefaultStrategyConfig(lang string) StrategyConfig {
PriceRankingLimit: 10,
},
RiskControl: RiskControlConfig{
MaxPositions: 3, // Max 3 coins simultaneously (CODE ENFORCED)
BTCETHMaxLeverage: 5, // BTC/ETH exchange leverage (AI guided)
AltcoinMaxLeverage: 5, // Altcoin exchange leverage (AI guided)
BTCETHMaxPositionValueRatio: 5.0, // BTC/ETH: max position = 5x equity (CODE ENFORCED)
AltcoinMaxPositionValueRatio: 1.0, // Altcoin: max position = 1x equity (CODE ENFORCED)
MaxMarginUsage: 0.9, // Max 90% margin usage (CODE ENFORCED)
MinPositionSize: 12, // Min 12 USDT per position (CODE ENFORCED)
MinRiskRewardRatio: 3.0, // Min 3:1 profit/loss ratio (AI guided)
MinConfidence: 75, // Min 75% confidence (AI guided)
MaxPositions: 2, // Max 2 instruments simultaneously (CODE ENFORCED)
BTCETHMaxLeverage: 10, // BTC/ETH exchange leverage (AI guided)
AltcoinMaxLeverage: 10, // TradeFi exchange leverage (AI guided)
BTCETHMaxPositionValueRatio: 1.0, // Claw402 default: same cap across assets
AltcoinMaxPositionValueRatio: 1.0, // Claw402 default: same cap across assets
MaxMarginUsage: 0.35, // Max 35% margin usage (CODE ENFORCED)
MinPositionSize: 12, // Min 12 USDT per position (CODE ENFORCED)
MinRiskRewardRatio: 3.0, // Min 3:1 profit/loss ratio (AI guided)
MinConfidence: 78, // Min 78% confidence (AI guided)
},
}
if lang == "zh" {
config.PromptSections = PromptSectionsConfig{
RoleDefinition: `# 你是一个专业的 Hyperliquid USDC 多资产交易AI
RoleDefinition: `# 你是 NOFX Claw402 自动交易员
的任务是根据提供的市场数据做出交易决策。你可以分析并交易 Hyperliquid 上线的 USDC 永续合约,包括美股、大宗商品和加密资产。你是一个经验丰富的量化交易员,擅长跨资产技术分析和风险管理`,
TradingFrequency: `# ⏱️ 交易频率意识
只交易 Claw402.ai/Vergex 本轮榜单返回的 Hyperliquid 可交易标的。候选池来自 Claw402.ai/Vergex开仓前必须结合 Signal Lab、成本/清算热力图和原始 K 线判断`,
TradingFrequency: `# 交易频率
- 优秀交易员每天2-4笔 ≈ 每小时0.1-0.2笔
- 每小时超过2笔 = 过度交易
- 单笔持仓时间 ≥ 30-60分钟
如果你发现自己每个周期都在交易 → 标准太低如果持仓不到30分钟就平仓 → 太冲动。`,
EntryStandards: `# 🎯 入场标准(严格)
- 优先等待高质量机会,不需要每轮都交易。
- 先管理已有持仓,再考虑新开仓。
- 同一轮不要频繁开平同一标的。`,
EntryStandards: `# 入场标准
在多个信号共振时入场。自由使用任何有效的分析方法,避免单一指标、信号矛盾、横盘震荡、或平仓后立即重新开仓等低质量行为`,
DecisionProcess: `# 📋 决策流程
有 Claw402 Signal Lab、成本/清算热力图和原始 K 线大体一致时才开仓。Claw402 排名只是候选池,不是单独买入理由。任一关键数据缺失或冲突时,默认等待`,
DecisionProcess: `# 决策流程
1. 检查持仓 → 是否止盈/止损
2. 扫描候选币种 + 多时间框架 → 是否存在强信号
3. 先写思维链再输出结构化JSON`,
1. 检查已有持仓,先决定止盈止损或继续持有。
2. 从 Claw402 榜单取本轮候选,并对每个候选读取 Claw402 Ranking、Signal Lab、Cost/Liquidation Heatmap。
3. 用原始 K 线确认入场位置、止损和止盈。
4. 输出简洁 reasoning 和严格 JSON。`,
}
} else {
config.PromptSections = PromptSectionsConfig{
RoleDefinition: `# You are a professional Hyperliquid USDC multi-asset trading AI
RoleDefinition: `# You are the NOFX Claw402 auto-trader
Your task is to make trading decisions based on the provided market data. You can analyze and trade Hyperliquid-listed USDC perpetual markets, including US equities, commodities and crypto assets. You are an experienced quantitative trader skilled in cross-asset technical analysis and risk management.`,
TradingFrequency: `# ⏱️ Trading Frequency Awareness
Trade Hyperliquid Claw402-ranked instruments only. The candidate pool comes from Claw402.ai/Vergex; before opening a position, combine Signal Lab, cost/liquidation heatmap and raw candles.`,
TradingFrequency: `# Trading Frequency
- Excellent trader: 2-4 trades per day ≈ 0.1-0.2 trades per hour
- >2 trades per hour = overtrading
- Single position holding time ≥ 30-60 minutes
If you find yourself trading every cycle → standards are too low; if closing positions in <30 minutes → too impulsive.`,
EntryStandards: `# 🎯 Entry Standards (Strict)
- Wait for quality; you do not need to trade every cycle.
- Manage existing positions before opening new ones.
- Do not churn in and out of the same symbol in one cycle.`,
EntryStandards: `# Entry Standards
Only enter positions when multiple signals resonate. Freely use any effective analysis methods, avoid low-quality behaviors such as single indicators, contradictory signals, sideways oscillation, or immediately restarting after closing positions.`,
DecisionProcess: `# 📋 Decision Process
Open only when Claw402 Signal Lab, cost/liquidation heatmap and raw candles broadly agree. Ranking defines the candidate pool, not a standalone entry reason. Wait when key data is missing or contradictory.`,
DecisionProcess: `# Decision Process
1. Check positions → whether to take profit/stop loss
2. Scan candidate coins + multi-timeframe → whether strong signals exist
3. Write chain of thought first, then output structured JSON`,
1. Check current positions first: take profit, stop loss or hold.
2. Pull this cycle's Claw402 board and read Claw402 Ranking, Signal Lab and Cost/Liquidation Heatmap for each candidate.
3. Use raw candles to confirm entry, stop and target.
4. Output concise reasoning and strict JSON.`,
}
}
@@ -1461,6 +1506,8 @@ func (c *StrategyConfig) getEffectiveCoinCount() int {
count = c.CoinSource.OILowLimit
case "hyper_rank":
count = c.CoinSource.HyperRankLimit
case "vergex_signal":
count = c.CoinSource.VergexLimit
case "hyper_main":
count = c.CoinSource.HyperMainLimit
case "hyper_all":