mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-16 17:12:25 +08:00
feat: simplify Claw402 autopilot trading flow
This commit is contained in:
124
kernel/engine_prompt_test.go
Normal file
124
kernel/engine_prompt_test.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"nofx/store"
|
||||
)
|
||||
|
||||
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 = "只做多,不做空。"
|
||||
|
||||
engine := NewStrategyEngine(&cfg)
|
||||
prompt := engine.BuildSystemPrompt(30, "balanced")
|
||||
|
||||
if !strings.Contains(prompt, "NOFX Claw402 auto-trader") {
|
||||
t.Fatalf("prompt did not use the Claw402/Vergex TradeFi role:\n%s", prompt)
|
||||
}
|
||||
if !strings.Contains(prompt, "Claw402.ai Signal Ranking") || !strings.Contains(prompt, "Signal Lab") || !strings.Contains(prompt, "Cost/Liquidation Heatmap") {
|
||||
t.Fatalf("prompt is missing Claw402/Vergex detail data guidance:\n%s", prompt)
|
||||
}
|
||||
if !strings.Contains(prompt, "open_short") {
|
||||
t.Fatalf("prompt should explicitly allow short entries:\n%s", prompt)
|
||||
}
|
||||
if !strings.Contains(prompt, "Direction must be data-driven") {
|
||||
t.Fatalf("prompt should explain that direction is data-driven, not long-only:\n%s", prompt)
|
||||
}
|
||||
if !strings.Contains(prompt, "every open position must use exactly 10x") {
|
||||
t.Fatalf("prompt should force 10x leverage for Claw402 opens:\n%s", prompt)
|
||||
}
|
||||
if !strings.Contains(prompt, "use the full max notional per position") {
|
||||
t.Fatalf("prompt should force full-size Claw402 opens:\n%s", prompt)
|
||||
}
|
||||
if containsCJK(prompt) {
|
||||
t.Fatalf("system prompt must be English-only, got CJK text:\n%s", prompt)
|
||||
}
|
||||
legacyPhrases := []string{
|
||||
"Hyperliquid USDC 多资产交易AI",
|
||||
"只做多",
|
||||
"山寨币",
|
||||
"BTC/ETH",
|
||||
"LONG-ONLY",
|
||||
"Do not short",
|
||||
"MUST open a long",
|
||||
}
|
||||
for _, phrase := range legacyPhrases {
|
||||
if strings.Contains(prompt, phrase) {
|
||||
t.Fatalf("prompt still contains legacy phrase %q:\n%s", phrase, prompt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildSystemPromptFallsBackToEnglishWhenConfiguredLanguageIsChinese(t *testing.T) {
|
||||
cfg := store.GetDefaultStrategyConfig("zh")
|
||||
cfg.CoinSource.SourceType = "static"
|
||||
cfg.CoinSource.StaticCoins = []string{"BTCUSDT", "ETHUSDT"}
|
||||
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 = "中文偏好不应进入系统提示词。"
|
||||
|
||||
engine := NewStrategyEngine(&cfg)
|
||||
prompt := engine.BuildSystemPrompt(30, "balanced")
|
||||
|
||||
required := []string{
|
||||
"Data Dictionary & Trading Rules",
|
||||
"You are a professional Hyperliquid USDC multi-asset trading AI",
|
||||
"Trading Frequency Awareness",
|
||||
"Entry Standards",
|
||||
"Decision Process",
|
||||
}
|
||||
for _, phrase := range required {
|
||||
if !strings.Contains(prompt, phrase) {
|
||||
t.Fatalf("English fallback prompt missing %q:\n%s", phrase, prompt)
|
||||
}
|
||||
}
|
||||
if containsCJK(prompt) {
|
||||
t.Fatalf("system prompt must be English-only, got CJK text:\n%s", prompt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildSystemPromptDoesNotForceLongOnlyForSingleXYZ(t *testing.T) {
|
||||
prompt := buildXYZStockCustomPrompt("XYZ:INTC")
|
||||
|
||||
required := []string{
|
||||
"DIRECTIONAL, SIGNAL-DRIVEN",
|
||||
"You may open long or short",
|
||||
"open_short",
|
||||
}
|
||||
for _, phrase := range required {
|
||||
if !strings.Contains(prompt, phrase) {
|
||||
t.Fatalf("single XYZ prompt missing %q:\n%s", phrase, prompt)
|
||||
}
|
||||
}
|
||||
|
||||
forbidden := []string{
|
||||
"LONG-ONLY",
|
||||
"Do not short",
|
||||
"MUST open a long",
|
||||
"Probing > waiting",
|
||||
}
|
||||
for _, phrase := range forbidden {
|
||||
if strings.Contains(prompt, phrase) {
|
||||
t.Fatalf("single XYZ prompt still contains forced-long phrase %q:\n%s", phrase, prompt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func containsCJK(text string) bool {
|
||||
for _, r := range text {
|
||||
if r >= 0x4E00 && r <= 0x9FFF {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user