Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"nofx/logger"
"nofx/manager"
"nofx/market"
"nofx/provider/coinank"
"nofx/provider/coinank/coinank_api"
"nofx/provider/coinank/coinank_enum"
"nofx/store"
"nofx/trader"
Expand Down Expand Up @@ -2321,11 +2321,8 @@ func (s *Server) handleKlines(c *gin.Context) {
c.JSON(http.StatusOK, klines)
}

// getKlinesFromCoinank fetches kline data from coinank API for multiple exchanges
// getKlinesFromCoinank fetches kline data from coinank free/open API for multiple exchanges
func (s *Server) getKlinesFromCoinank(symbol, interval, exchange string, limit int) ([]market.Kline, error) {
// Import coinank packages
coinankClient := coinank.NewCoinankClient(coinank_enum.MainUrl, "0cccbd7992754b67b1848c6746c0fce0")

// Map exchange string to coinank enum
var coinankExchange coinank_enum.Exchange
switch strings.ToLower(exchange) {
Expand Down Expand Up @@ -2397,12 +2394,34 @@ func (s *Server) getKlinesFromCoinank(symbol, interval, exchange string, limit i
return nil, fmt.Errorf("unsupported interval for coinank: %s", interval)
}

// Call coinank API
// Convert symbol format for different exchanges
// OKX uses "BTC-USDT-SWAP" format instead of "BTCUSDT"
apiSymbol := symbol
if coinankExchange == coinank_enum.Okex {
// Convert BTCUSDT -> BTC-USDT-SWAP
if strings.HasSuffix(symbol, "USDT") {
base := strings.TrimSuffix(symbol, "USDT")
apiSymbol = fmt.Sprintf("%s-USDT-SWAP", base)
}
}

// Call coinank free/open API (no authentication required)
ctx := context.Background()
endTime := time.Now().UnixMilli()
coinankKlines, err := coinankClient.Kline(ctx, symbol, coinankExchange, 0, endTime, limit, coinankInterval)
if err != nil {
return nil, fmt.Errorf("coinank API error: %w", err)
ts := time.Now().UnixMilli()
// Use "To" side to search backward from current time (get historical klines)
coinankKlines, err := coinank_api.Kline(ctx, apiSymbol, coinankExchange, ts, coinank_enum.To, limit, coinankInterval)
if err != nil {
// Free API doesn't support all exchanges (e.g., OKX, Bitget)
// Fallback to Binance data as reference
if coinankExchange != coinank_enum.Binance {
logger.Warnf("⚠️ CoinAnk free API doesn't support %s, falling back to Binance data", coinankExchange)
coinankKlines, err = coinank_api.Kline(ctx, symbol, coinank_enum.Binance, ts, coinank_enum.To, limit, coinankInterval)
if err != nil {
return nil, fmt.Errorf("coinank API error (fallback): %w", err)
}
} else {
return nil, fmt.Errorf("coinank API error: %w", err)
}
}

// Convert coinank kline format to market.Kline format
Expand Down
15 changes: 6 additions & 9 deletions market/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"fmt"
"io"
"nofx/logger"
"nofx/provider/coinank"
"nofx/provider/coinank/coinank_api"
"nofx/provider/coinank/coinank_enum"
"math"
"strconv"
Expand All @@ -25,13 +25,9 @@ type FundingRateCache struct {
var (
fundingRateMap sync.Map // map[string]*FundingRateCache
frCacheTTL = 1 * time.Hour
coinankClient *coinank.CoinankClient // Global CoinAnk client for kline data
)

// Initialize CoinAnk client
func init() {
coinankClient = coinank.NewCoinankClient(coinank_enum.MainUrl, "0cccbd7992754b67b1848c6746c0fce0")
}
// Note: Kline data now uses free/open API (coinank_api.Kline) which doesn't require authentication

// getKlinesFromCoinAnk fetches kline data from CoinAnk API (replacement for WSMonitorCli)
func getKlinesFromCoinAnk(symbol, interval string, limit int) ([]Kline, error) {
Expand Down Expand Up @@ -70,10 +66,11 @@ func getKlinesFromCoinAnk(symbol, interval string, limit int) ([]Kline, error) {
return nil, fmt.Errorf("unsupported interval: %s", interval)
}

// Call CoinAnk API (default to Binance exchange for compatibility)
// Call CoinAnk free/open API (no authentication required)
ctx := context.Background()
endTime := time.Now().UnixMilli()
coinankKlines, err := coinankClient.Kline(ctx, symbol, coinank_enum.Binance, 0, endTime, limit, coinankInterval)
ts := time.Now().UnixMilli()
// Use "To" side to search backward from current time (get historical klines)
coinankKlines, err := coinank_api.Kline(ctx, symbol, coinank_enum.Binance, ts, coinank_enum.To, limit, coinankInterval)
if err != nil {
return nil, fmt.Errorf("CoinAnk API error: %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ function getExchangeDisplayNameFromList(
: typeName
}

// Helper function to get exchange type from exchange ID (UUID) - for TradingView charts
// Helper function to get exchange type from exchange ID (UUID) - for kline charts
function getExchangeTypeFromList(
exchangeId: string | undefined,
exchanges: Exchange[] | undefined
): string {
if (!exchangeId) return 'BINANCE'
if (!exchangeId) return 'binance'
const exchange = exchanges?.find((e) => e.id === exchangeId)
if (!exchange) return 'BINANCE' // Default to BINANCE for charts
return exchange.exchange_type?.toUpperCase() || 'BINANCE'
if (!exchange) return 'binance' // Default to binance for charts
return exchange.exchange_type?.toLowerCase() || 'binance'
}

// Helper function to check if exchange is a perp-dex type (wallet-based)
Expand Down
36 changes: 36 additions & 0 deletions web/src/components/AdvancedChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,42 @@ export function AdvancedChart({
<span className="text-xs px-2 py-0.5 rounded" style={{ background: '#2B3139', color: '#848E9C' }}>
{interval}
</span>
{/* 交易所标识 */}
<span
className="text-xs px-2 py-0.5 rounded font-medium uppercase"
style={{
background: exchange === 'binance' ? 'rgba(243, 186, 47, 0.15)' :
exchange === 'bybit' ? 'rgba(247, 147, 26, 0.15)' :
exchange === 'okx' ? 'rgba(0, 180, 255, 0.15)' :
exchange === 'bitget' ? 'rgba(0, 212, 170, 0.15)' :
exchange === 'hyperliquid' ? 'rgba(80, 227, 194, 0.15)' :
exchange === 'aster' ? 'rgba(138, 43, 226, 0.15)' :
'rgba(255, 255, 255, 0.1)',
color: exchange === 'binance' ? '#F3BA2F' :
exchange === 'bybit' ? '#F7931A' :
exchange === 'okx' ? '#00B4FF' :
exchange === 'bitget' ? '#00D4AA' :
exchange === 'hyperliquid' ? '#50E3C2' :
exchange === 'aster' ? '#8A2BE2' :
'#848E9C',
border: `1px solid ${
exchange === 'binance' ? 'rgba(243, 186, 47, 0.3)' :
exchange === 'bybit' ? 'rgba(247, 147, 26, 0.3)' :
exchange === 'okx' ? 'rgba(0, 180, 255, 0.3)' :
exchange === 'bitget' ? 'rgba(0, 212, 170, 0.3)' :
exchange === 'hyperliquid' ? 'rgba(80, 227, 194, 0.3)' :
exchange === 'aster' ? 'rgba(138, 43, 226, 0.3)' :
'rgba(255, 255, 255, 0.2)'
}`
}}
title={['bitget', 'lighter'].includes(exchange?.toLowerCase() || '')
? 'Data source: Binance (fallback)' : undefined}
>
{exchange}
{['bitget', 'lighter'].includes(exchange?.toLowerCase() || '') && (
<span className="ml-1 text-[9px] opacity-60">*</span>
)}
</span>
</div>

<div className="flex items-center gap-2">
Expand Down
1 change: 1 addition & 0 deletions web/src/components/ChartTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export function ChartTabs({ traderId, selectedSymbol, updateKey, exchangeId }: C
interval={interval}
traderID={traderId}
height={550}
exchange={exchangeId || 'binance'}
onSymbolChange={setChartSymbol}
/>
</motion.div>
Expand Down
Loading