mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-10 06:20:58 +08:00
feat: migrate to CoinAnk API and improve chart UI
- Chart improvements: professional styling, popular symbols quick selection, simplified B/S legend - Data source migration: use CoinAnk API exclusively for all kline data - Code cleanup: remove Binance WebSocket cache and related code (websocket_client.go, combined_streams.go, monitor.go) - Log optimization: reduce hook spam, suppress 404 errors, increase P&L diff threshold - Lighter integration: add order sync functionality, fix market order precision - Remove ticker merge logic for simplicity
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { EquityChart } from './EquityChart'
|
||||
import { TradingViewChart } from './TradingViewChart'
|
||||
import { AdvancedChart } from './AdvancedChart'
|
||||
import { useLanguage } from '../contexts/LanguageContext'
|
||||
import { t } from '../i18n/translations'
|
||||
import { BarChart3, CandlestickChart } from 'lucide-react'
|
||||
@@ -14,11 +14,24 @@ interface ChartTabsProps {
|
||||
}
|
||||
|
||||
type ChartTab = 'equity' | 'kline'
|
||||
type Interval = '1m' | '5m' | '15m' | '30m' | '1h' | '4h' | '1d'
|
||||
|
||||
const INTERVALS: { value: Interval; label: string }[] = [
|
||||
{ value: '1m', label: '1m' },
|
||||
{ value: '5m', label: '5m' },
|
||||
{ value: '15m', label: '15m' },
|
||||
{ value: '30m', label: '30m' },
|
||||
{ value: '1h', label: '1h' },
|
||||
{ value: '4h', label: '4h' },
|
||||
{ value: '1d', label: '1d' },
|
||||
]
|
||||
|
||||
export function ChartTabs({ traderId, selectedSymbol, updateKey, exchangeId }: ChartTabsProps) {
|
||||
const { language } = useLanguage()
|
||||
const [activeTab, setActiveTab] = useState<ChartTab>('equity')
|
||||
const [chartSymbol, setChartSymbol] = useState<string>('BTCUSDT')
|
||||
const [interval, setInterval] = useState<Interval>('5m')
|
||||
const [symbolInput, setSymbolInput] = useState('')
|
||||
|
||||
// 当从外部选择币种时,自动切换到K线图
|
||||
useEffect(() => {
|
||||
@@ -29,45 +42,105 @@ export function ChartTabs({ traderId, selectedSymbol, updateKey, exchangeId }: C
|
||||
}
|
||||
}, [selectedSymbol, updateKey])
|
||||
|
||||
// 处理手动输入币种
|
||||
const handleSymbolSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (symbolInput.trim()) {
|
||||
const symbol = symbolInput.trim().toUpperCase()
|
||||
setChartSymbol(symbol)
|
||||
setSymbolInput('')
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[ChartTabs] rendering, activeTab:', activeTab)
|
||||
|
||||
return (
|
||||
<div className="binance-card">
|
||||
{/* Tab Headers - 这是Tab切换按钮区域 */}
|
||||
{/* Tab Headers - 专业化工具栏 */}
|
||||
<div
|
||||
className="flex items-center gap-2 p-3"
|
||||
className="flex items-center justify-between px-4 py-2"
|
||||
style={{
|
||||
borderBottom: '1px solid #2B3139',
|
||||
background: '#0B0E11',
|
||||
background: 'linear-gradient(180deg, #1A1E23 0%, #0B0E11 100%)',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => {
|
||||
console.log('[ChartTabs] switching to equity')
|
||||
setActiveTab('equity')
|
||||
}}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all ${activeTab === 'equity'
|
||||
? 'bg-yellow-500/10 text-yellow-500 border border-yellow-500/30 shadow-[0_0_10px_rgba(252,213,53,0.15)]'
|
||||
: 'text-gray-400 hover:text-white hover:bg-white/5 border border-transparent'
|
||||
}`}
|
||||
>
|
||||
<BarChart3 className="w-4 h-4" />
|
||||
{t('accountEquityCurve', language)}
|
||||
</button>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<button
|
||||
onClick={() => {
|
||||
console.log('[ChartTabs] switching to equity')
|
||||
setActiveTab('equity')
|
||||
}}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-all ${activeTab === 'equity'
|
||||
? 'bg-yellow-500/15 text-yellow-400 border border-yellow-500/40'
|
||||
: 'text-gray-400 hover:text-gray-200 hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
<BarChart3 className="w-3.5 h-3.5" />
|
||||
<span>{t('accountEquityCurve', language)}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
console.log('[ChartTabs] switching to kline')
|
||||
setActiveTab('kline')
|
||||
}}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all ${activeTab === 'kline'
|
||||
? 'bg-yellow-500/10 text-yellow-500 border border-yellow-500/30 shadow-[0_0_10px_rgba(252,213,53,0.15)]'
|
||||
: 'text-gray-400 hover:text-white hover:bg-white/5 border border-transparent'
|
||||
}`}
|
||||
>
|
||||
<CandlestickChart className="w-4 h-4" />
|
||||
{t('marketChart', language)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
console.log('[ChartTabs] switching to kline')
|
||||
setActiveTab('kline')
|
||||
}}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-all ${activeTab === 'kline'
|
||||
? 'bg-yellow-500/15 text-yellow-400 border border-yellow-500/40'
|
||||
: 'text-gray-400 hover:text-gray-200 hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
<CandlestickChart className="w-3.5 h-3.5" />
|
||||
<span>{t('marketChart', language)}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 币种选择器和时间周期选择器 - 仅在K线图模式下显示 */}
|
||||
{activeTab === 'kline' && (
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 当前币种显示 */}
|
||||
<div className="px-2.5 py-1 bg-[#1A1E23] border border-[#2B3139] rounded text-xs font-bold text-yellow-400">
|
||||
{chartSymbol}
|
||||
</div>
|
||||
|
||||
<div className="w-px h-4 bg-[#2B3139]"></div>
|
||||
|
||||
{/* 时间周期选择器 - 更紧凑专业 */}
|
||||
<div className="flex items-center gap-0.5">
|
||||
{INTERVALS.map((int) => (
|
||||
<button
|
||||
key={int.value}
|
||||
onClick={() => setInterval(int.value)}
|
||||
className={`px-2 py-1 text-[10px] font-medium transition-all ${
|
||||
interval === int.value
|
||||
? 'bg-yellow-500/20 text-yellow-400 rounded'
|
||||
: 'text-gray-500 hover:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
{int.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="w-px h-4 bg-[#2B3139]"></div>
|
||||
|
||||
{/* 币种输入框 - 更紧凑 */}
|
||||
<form onSubmit={handleSymbolSubmit} className="flex items-center gap-1.5">
|
||||
<input
|
||||
type="text"
|
||||
value={symbolInput}
|
||||
onChange={(e) => setSymbolInput(e.target.value)}
|
||||
placeholder="输入币种..."
|
||||
className="px-2 py-1 bg-[#1A1E23] border border-[#2B3139] rounded text-[11px] text-white placeholder-gray-600 focus:outline-none focus:border-yellow-500/50 w-24"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-2 py-1 bg-yellow-500/15 text-yellow-400 border border-yellow-500/30 rounded text-[10px] font-medium hover:bg-yellow-500/25 transition-all"
|
||||
>
|
||||
GO
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
@@ -86,19 +159,19 @@ export function ChartTabs({ traderId, selectedSymbol, updateKey, exchangeId }: C
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key={`kline-${chartSymbol}-${exchangeId}`}
|
||||
key={`kline-${chartSymbol}-${interval}-${exchangeId}`}
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="h-full"
|
||||
>
|
||||
<TradingViewChart
|
||||
height={400}
|
||||
embedded
|
||||
defaultSymbol={chartSymbol}
|
||||
defaultExchange={exchangeId}
|
||||
key={`${chartSymbol}-${exchangeId}-${updateKey || ''}`}
|
||||
<AdvancedChart
|
||||
symbol={chartSymbol}
|
||||
interval={interval}
|
||||
traderID={traderId}
|
||||
height={550}
|
||||
onSymbolChange={setChartSymbol}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user