feat(agent): surface Hyperliquid stock trading context

- Add stock symbol panel and agent chat page wiring

- Update onboarding and tool visibility for focused trader flows

- Tighten related tests around configuration and trader scope
This commit is contained in:
tinklefund
2026-05-25 01:25:10 +08:00
parent 5bdffee3b0
commit f4ee723aa2
8 changed files with 386 additions and 24 deletions

View File

@@ -286,7 +286,7 @@ func TestLoadExchangeOptionsHidesInvisibleExchangeRows(t *testing.T) {
}).Error; err != nil {
t.Fatalf("seed legacy hidden exchange: %v", err)
}
if _, err := st.Exchange().Create("default", "okx", "我的主力OKX账户", true, "api-test", "secret-test", "pass-test", false, "", false, "", "", "", "", "", "", 0); err != nil {
if _, err := st.Exchange().Create("default", "okx", "我的主力OKX账户", true, "api-test", "secret-test", "pass-test", false, "", false, false, "", "", "", "", "", "", 0); err != nil {
t.Fatalf("create visible exchange: %v", err)
}
@@ -307,7 +307,7 @@ func TestDescribeExchangeIncludesTypeSpecificVisibleFields(t *testing.T) {
}
a := New(nil, st, DefaultConfig(), slog.Default())
hyperID, err := st.Exchange().Create("default", "hyperliquid", "Dex Pro", true, "hyper-api-key", "", "", true, "0xabc", true, "", "", "", "", "", "", 0)
hyperID, err := st.Exchange().Create("default", "hyperliquid", "Dex Pro", true, "hyper-api-key", "", "", true, "0xabc", true, false, "", "", "", "", "", "", 0)
if err != nil {
t.Fatalf("seed hyperliquid exchange: %v", err)
}
@@ -321,7 +321,7 @@ func TestDescribeExchangeIncludesTypeSpecificVisibleFields(t *testing.T) {
}
}
lighterID, err := st.Exchange().Create("default", "lighter", "Lighter Main", false, "", "", "", false, "", true, "", "", "", "wallet-1", "", "lighter-secret", 7)
lighterID, err := st.Exchange().Create("default", "lighter", "Lighter Main", false, "", "", "", false, "", true, false, "", "", "", "wallet-1", "", "lighter-secret", 7)
if err != nil {
t.Fatalf("seed lighter exchange: %v", err)
}
@@ -463,7 +463,7 @@ func TestToolUpdateTraderRejectsRenameOutsideManualPanel(t *testing.T) {
if err := st.AIModel().UpdateWithName("default", "default_deepseek", "DeepSeek", true, "sk-test-12345", "", "deepseek-chat"); err != nil {
t.Fatalf("seed model: %v", err)
}
exchangeID, err := st.Exchange().Create("default", "binance", "Main", true, "api-test", "secret-test", "", false, "", false, "", "", "", "", "", "", 0)
exchangeID, err := st.Exchange().Create("default", "binance", "Main", true, "api-test", "secret-test", "", false, "", false, false, "", "", "", "", "", "", 0)
if err != nil {
t.Fatalf("seed exchange: %v", err)
}
@@ -515,7 +515,7 @@ func TestToolCreateTraderResponseHidesLegacyTraderTuningFields(t *testing.T) {
if err := st.AIModel().UpdateWithName("default", "default_deepseek", "DeepSeek", true, "sk-test-12345", "", "deepseek-chat"); err != nil {
t.Fatalf("seed model: %v", err)
}
exchangeID, err := st.Exchange().Create("default", "binance", "Main", true, "api-test", "secret-test", "", false, "", false, "", "", "", "", "", "", 0)
exchangeID, err := st.Exchange().Create("default", "binance", "Main", true, "api-test", "secret-test", "", false, "", false, false, "", "", "", "", "", "", 0)
if err != nil {
t.Fatalf("seed exchange: %v", err)
}
@@ -566,7 +566,7 @@ func TestToolCreateTraderAutoReadsInitialBalanceFromExchange(t *testing.T) {
if err := st.AIModel().UpdateWithName("default", "default_deepseek", "DeepSeek", true, "sk-test-12345", "", "deepseek-chat"); err != nil {
t.Fatalf("seed model: %v", err)
}
exchangeID, err := st.Exchange().Create("default", "binance", "Main", true, "api-test", "secret-test", "", false, "", false, "", "", "", "", "", "", 0)
exchangeID, err := st.Exchange().Create("default", "binance", "Main", true, "api-test", "secret-test", "", false, "", false, false, "", "", "", "", "", "", 0)
if err != nil {
t.Fatalf("seed exchange: %v", err)
}

View File

@@ -455,7 +455,7 @@ func (a *Agent) saveSetupExchange(storeUserID string, state *SetupState) (string
storeUserID, ex.ID, true,
apiKey, apiSecret, passphrase,
false,
hlWallet, hlUnified,
hlWallet, hlUnified, false,
"", "", "",
"", "", "", 0,
); err != nil {
@@ -472,7 +472,7 @@ func (a *Agent) saveSetupExchange(storeUserID string, state *SetupState) (string
true,
apiKey, apiSecret, passphrase,
false,
hlWallet, hlUnified,
hlWallet, hlUnified, false,
"", "", "",
"", "", "", 0,
)

View File

@@ -1516,6 +1516,7 @@ func (a *Agent) toolManageExchangeConfig(storeUserID, argsJSON string) string {
testnet,
strings.TrimSpace(args.HyperliquidWalletAddr),
unified,
false,
strings.TrimSpace(args.AsterUser),
strings.TrimSpace(args.AsterSigner),
strings.TrimSpace(args.AsterPrivateKey),
@@ -1647,6 +1648,7 @@ func (a *Agent) toolManageExchangeConfig(storeUserID, argsJSON string) string {
testnet,
hyperWallet,
unified,
existing.HyperliquidBuilderApproved,
asterUser,
asterSigner,
strings.TrimSpace(args.AsterPrivateKey),
@@ -2518,9 +2520,13 @@ func (a *Agent) toolStartTrader(storeUserID, traderID string) string {
if a.traderManager == nil {
return `{"error":"trader manager unavailable"}`
}
if _, err := a.store.Trader().GetFullConfig(storeUserID, traderID); err != nil {
fullCfg, err := a.store.Trader().GetFullConfig(storeUserID, traderID)
if err != nil {
return fmt.Sprintf(`{"error":"trader not found or inaccessible: %s"}`, err)
}
if fullCfg != nil && fullCfg.Exchange != nil && fullCfg.Exchange.ExchangeType == "hyperliquid" && !fullCfg.Exchange.HyperliquidBuilderApproved {
return `{"error":"Hyperliquid trading authorization is incomplete; reconnect Hyperliquid wallet and complete trading authorization before starting this trader"}`
}
if existing, err := a.traderManager.GetTrader(traderID); err == nil {
if running, ok := existing.GetStatus()["is_running"].(bool); ok && running {
return `{"error":"trader is already running"}`

View File

@@ -372,7 +372,7 @@ func TestHydrateCreateTraderSlotReferencesNormalizesExchangeIDFromVisibleName(t
}
a := New(nil, st, DefaultConfig(), slog.Default())
exchangeID, err := st.Exchange().Create("default", "okx", "小偶", true, "api-test", "secret-test", "pass", false, "", false, "", "", "", "", "", "", 0)
exchangeID, err := st.Exchange().Create("default", "okx", "小偶", true, "api-test", "secret-test", "pass", false, "", false, false, "", "", "", "", "", "", 0)
if err != nil {
t.Fatalf("seed exchange: %v", err)
}
@@ -737,7 +737,7 @@ func TestBuildTraderCreateMissingPromptListsAllMissingSlots(t *testing.T) {
if err := st.AIModel().UpdateWithName("default", "default_deepseek", "DeepSeek AI", true, "sk-test-12345", "", "deepseek-chat"); err != nil {
t.Fatalf("seed model: %v", err)
}
exchangeID, err := st.Exchange().Create("default", "okx", "OKX 主账户", true, "api-test", "secret-test", "pass", false, "", false, "", "", "", "", "", "", 0)
exchangeID, err := st.Exchange().Create("default", "okx", "OKX 主账户", true, "api-test", "secret-test", "pass", false, "", false, false, "", "", "", "", "", "", 0)
if err != nil {
t.Fatalf("seed exchange: %v", err)
}