feat(hyperliquid): add stock symbol market data support

- Add Hyperliquid/XYZ symbol normalization tests and backend coverage

- Extend kline and market data lookup paths for US stock symbols

- Wire frontend data API types for stock-oriented market requests
This commit is contained in:
tinklefund
2026-05-25 01:24:49 +08:00
parent 908fc09aca
commit f37fc9f887
12 changed files with 551 additions and 109 deletions

View File

@@ -6,6 +6,7 @@ import (
"io"
"math"
"nofx/logger"
"nofx/provider/hyperliquid"
"strconv"
"strings"
"sync"
@@ -229,7 +230,7 @@ func GetWithTimeframes(symbol string, timeframes []string, primaryTimeframe stri
currentRSI7 := calculateRSI(primaryKlines, 7)
// Calculate price changes
priceChange1h := calculatePriceChangeByBars(primaryKlines, primaryTimeframe, 60) // 1 hour
priceChange1h := calculatePriceChangeByBars(primaryKlines, primaryTimeframe, 60) // 1 hour
priceChange4h := calculatePriceChangeByBars(primaryKlines, primaryTimeframe, 240) // 4 hours
// Get OI data
@@ -540,6 +541,9 @@ var xyzDexAssets = map[string]bool{
// IsXyzDexAsset checks if a symbol is an xyz dex asset
func IsXyzDexAsset(symbol string) bool {
base := strings.ToUpper(symbol)
if strings.HasSuffix(base, "-USDC") || strings.HasPrefix(strings.ToLower(base), "xyz:") {
return hyperliquid.IsXYZAsset(base)
}
// Remove any prefix/suffix
base = strings.TrimPrefix(base, "XYZ:")
for _, suffix := range []string{"USDT", "USD", "-USDC"} {
@@ -548,7 +552,7 @@ func IsXyzDexAsset(symbol string) bool {
break
}
}
return xyzDexAssets[base]
return xyzDexAssets[base] || hyperliquid.IsXYZAsset(base)
}
// Normalize normalizes symbol
@@ -556,22 +560,13 @@ func IsXyzDexAsset(symbol string) bool {
// For xyz dex assets (stocks, forex, commodities): uses xyz: prefix without USDT suffix
func Normalize(symbol string) string {
symbol = strings.ToUpper(symbol)
if strings.HasSuffix(symbol, "-USDC") {
return hyperliquid.FormatCoinForAPI(symbol)
}
// Check if this is an xyz dex asset
if IsXyzDexAsset(symbol) {
// Remove any xyz: prefix (case-insensitive) and USDT suffix, then add xyz: prefix
base := symbol
// Handle both lowercase and uppercase xyz: prefix
if strings.HasPrefix(strings.ToLower(base), "xyz:") {
base = base[4:] // Remove first 4 characters ("xyz:")
}
for _, suffix := range []string{"USDT", "USD", "-USDC"} {
if strings.HasSuffix(base, suffix) {
base = strings.TrimSuffix(base, suffix)
break
}
}
return "xyz:" + base
return hyperliquid.FormatCoinForAPI(symbol)
}
// Remove exchange-specific separators (Gate uses BTC_USDT, OKX uses BTC-USDT-SWAP)

View File

@@ -0,0 +1,26 @@
package market
import "testing"
func TestHyperliquidXYZAliasesNormalizeForAIDecisionData(t *testing.T) {
tests := []struct {
input string
normalized string
isXyzAsset bool
}{
{input: "SMSN-USDC", normalized: "xyz:SMSN", isXyzAsset: true},
{input: "SAMSUNG-USDC", normalized: "xyz:SMSN", isXyzAsset: true},
{input: "xyz:SMSN", normalized: "xyz:SMSN", isXyzAsset: true},
{input: "TESLA-USDC", normalized: "xyz:TSLA", isXyzAsset: true},
{input: "TSLA-USDC", normalized: "xyz:TSLA", isXyzAsset: true},
}
for _, tt := range tests {
if got := Normalize(tt.input); got != tt.normalized {
t.Fatalf("Normalize(%q) = %q, want %q", tt.input, got, tt.normalized)
}
if got := IsXyzDexAsset(tt.normalized); got != tt.isXyzAsset {
t.Fatalf("IsXyzDexAsset(%q) = %v, want %v", tt.normalized, got, tt.isXyzAsset)
}
}
}