mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-11 06:46:59 +08:00
fix(market): route Hyperliquid USDC perps correctly + symbol fuzzy match
A single-symbol QNT-USDC trader produced 0 candidate coins, 500 errors
from Hyperliquid, and "🚫 Dropped AI decision" warnings — the agent had
no market data to reason about, so it sat in `wait` forever. Three
chained bugs:
1. provider/hyperliquid/kline.go (IsXYZAsset / FormatCoinForAPI):
asset detection required the base symbol to appear in the hardcoded
StockPerpsSymbols / XYZOtherSymbols / display-alias lists. QNT, ARM,
and every other newly-listed Hyperliquid USDC perp wasn't in the
list, so the code routed them to the crypto path (CoinAnk) which
doesn't have them. Now the `-USDC` suffix and `xyz:` prefix are
trusted as definitive Hyperliquid signals — these tokens are
Hyperliquid-specific and new listings don't require a code change.
The hardcoded lists are kept as fallbacks for bare base symbols.
2. market/data_klines.go (getKlinesFromHyperliquid): the function
stripped the `xyz:` prefix before calling GetCandles, defeating
GetCandles's own FormatCoinForAPI logic. With the hardcoded list
missing the new ticker, FormatCoinForAPI couldn't re-add the prefix
and the request hit Hyperliquid's crypto perp endpoint — which
returns 500 for stock-only tickers. Pass the symbol through as-is.
3. trader/auto_trader_loop.go (filterDecisionsToStrategyUniverse): the
AI sometimes echoes a candidate as "QNTUSDC" / "QNT-USDC" / "QNTUSDT"
/ bare "QNT" instead of the canonical "xyz:QNT" we supplied. Strict
exact-match was dropping all of them. Added a base-level key
(strips xyz:, -USDC, -USDT, USDC, USDT, USD; normalizes display
aliases like ROBINHOOD → HOOD) and rewrites the matched decision's
symbol to the canonical form so the order pipeline downstream sees
the format it expects.
After this, a single-symbol stock trader fetches real K-line data from
Hyperliquid, the AI sees real candidates, and decisions get executed
on-chain instead of silently filtered.
This commit is contained in:
@@ -460,8 +460,21 @@ func IsStockPerp(symbol string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsXYZAsset checks if a symbol is on the xyz dex (stocks, forex, commodities)
|
||||
// IsXYZAsset checks if a symbol is on the xyz dex (stocks, forex, commodities).
|
||||
//
|
||||
// Detection is suffix-driven first, hardcoded-list second:
|
||||
// 1. `xyz:` prefix or `-USDC` suffix are unambiguous Hyperliquid signals —
|
||||
// the only place those tokens originate is the Hyperliquid USDC board.
|
||||
// This unblocks newly-listed stock perpetuals (QNT, ARM, ...) without
|
||||
// requiring a code change every time Hyperliquid adds a ticker.
|
||||
// 2. Bare bases (e.g. "QNT" with no qualifying suffix) still fall back to
|
||||
// the hardcoded StockPerpsSymbols / XYZOtherSymbols / display alias lists
|
||||
// so callers passing pre-normalized base symbols continue to work.
|
||||
func IsXYZAsset(symbol string) bool {
|
||||
trimmed := strings.ToUpper(strings.TrimSpace(symbol))
|
||||
if strings.HasPrefix(strings.ToLower(trimmed), "xyz:") || strings.HasSuffix(trimmed, "-USDC") {
|
||||
return true
|
||||
}
|
||||
coin := NormalizeCoinBase(symbol)
|
||||
// Check stock perps
|
||||
for _, s := range StockPerpsSymbols {
|
||||
@@ -507,12 +520,23 @@ func NormalizeCoinBase(symbol string) string {
|
||||
return NormalizeXYZAlias(symbol)
|
||||
}
|
||||
|
||||
// FormatCoinForAPI formats the coin name for Hyperliquid API
|
||||
// Stock perps need xyz:SYMBOL format, crypto uses plain symbol
|
||||
// FormatCoinForAPI formats the coin name for Hyperliquid API.
|
||||
// Stock perps need xyz:SYMBOL format, crypto uses plain symbol.
|
||||
//
|
||||
// Decision order:
|
||||
// 1. `xyz:` prefix OR `-USDC` suffix on the original input ⇒ xyz asset
|
||||
// (these tokens are Hyperliquid-specific, so the answer is unambiguous
|
||||
// regardless of whether the base symbol appears in our hardcoded lists).
|
||||
// 2. After stripping suffixes, if the bare base matches a known xyz asset
|
||||
// (stock perps, forex, commodities, display aliases) ⇒ also xyz.
|
||||
// 3. Otherwise crypto.
|
||||
func FormatCoinForAPI(symbol string) string {
|
||||
hasExplicitXYZ := strings.HasPrefix(strings.ToLower(strings.TrimSpace(symbol)), "xyz:")
|
||||
trimmed := strings.TrimSpace(symbol)
|
||||
upper := strings.ToUpper(trimmed)
|
||||
hasExplicitXYZ := strings.HasPrefix(strings.ToLower(trimmed), "xyz:")
|
||||
hasUSDCSuffix := strings.HasSuffix(upper, "-USDC")
|
||||
base := NormalizeCoinBase(symbol)
|
||||
if hasExplicitXYZ || IsXYZAsset(base) {
|
||||
if hasExplicitXYZ || hasUSDCSuffix || IsXYZAsset(base) {
|
||||
return "xyz:" + base
|
||||
}
|
||||
return base
|
||||
|
||||
Reference in New Issue
Block a user