feat: improve strategy studio and fix trader deletion bug

- Add strategy export/import functionality to Strategy Studio
- Fix trader deletion not removing from memory (competition page ghost data)
- Simplify TraderConfigViewModal: remove unused fields, show strategy name
- Improve quant data formatting: OI/Netflow multi-timeframe display
- Add configurable OI/Netflow toggles in indicator settings
- Clean up unused frontend components and dead code
This commit is contained in:
tinkle-community
2025-12-09 16:46:58 +08:00
parent 48792907b3
commit 9fa2432705
20 changed files with 258 additions and 2348 deletions

View File

@@ -1,5 +1,3 @@
import { useState } from 'react'
import { toast } from 'sonner'
import type { TraderConfigData } from '../types'
import { PunkAvatar, getTraderAvatar } from './PunkAvatar'
@@ -20,66 +18,20 @@ export function TraderConfigViewModal({
onClose,
traderData,
}: TraderConfigViewModalProps) {
const [copiedField, setCopiedField] = useState<string | null>(null)
if (!isOpen || !traderData) return null
const copyToClipboard = async (text: string, fieldName: string) => {
try {
await navigator.clipboard.writeText(text)
setCopiedField(fieldName)
setTimeout(() => setCopiedField(null), 2000)
toast.success('已复制到剪贴板')
} catch (error) {
console.error('Failed to copy:', error)
toast.error('复制失败,请手动复制')
}
}
const CopyButton = ({
text,
fieldName,
}: {
text: string
fieldName: string
}) => (
<button
onClick={() => copyToClipboard(text, fieldName)}
className="ml-2 px-2 py-1 text-xs rounded transition-all duration-200 hover:scale-105"
style={{
background:
copiedField === fieldName
? 'rgba(14, 203, 129, 0.1)'
: 'rgba(240, 185, 11, 0.1)',
color: copiedField === fieldName ? '#0ECB81' : '#F0B90B',
border: `1px solid ${copiedField === fieldName ? 'rgba(14, 203, 129, 0.3)' : 'rgba(240, 185, 11, 0.3)'}`,
}}
>
{copiedField === fieldName ? '✓ 已复制' : '📋 复制'}
</button>
)
const InfoRow = ({
label,
value,
copyable = false,
fieldName = '',
}: {
label: string
value: string | number | boolean
copyable?: boolean
fieldName?: string
}) => (
<div className="flex justify-between items-start py-2 border-b border-[#2B3139] last:border-b-0">
<span className="text-sm text-[#848E9C] font-medium">{label}</span>
<div className="flex items-center text-right">
<span className="text-sm text-[#EAECEF] font-mono">
{typeof value === 'boolean' ? (value ? '是' : '否') : value}
</span>
{copyable && typeof value === 'string' && value && (
<CopyButton text={value} fieldName={fieldName} />
)}
</div>
<span className="text-sm text-[#EAECEF] font-mono text-right">
{typeof value === 'boolean' ? (value ? '是' : '否') : value}
</span>
</div>
)
@@ -134,17 +86,9 @@ export function TraderConfigViewModal({
🤖
</h3>
<div className="space-y-3">
<InfoRow
label="交易员ID"
value={traderData.trader_id || ''}
copyable
fieldName="trader_id"
/>
<InfoRow
label="交易员名称"
value={traderData.trader_name}
copyable
fieldName="trader_name"
/>
<InfoRow
label="AI模型"
@@ -158,118 +102,41 @@ export function TraderConfigViewModal({
label="初始余额"
value={`$${traderData.initial_balance.toLocaleString()}`}
/>
</div>
</div>
{/* Trading Configuration */}
<div className="bg-[#0B0E11] border border-[#2B3139] rounded-lg p-5">
<h3 className="text-lg font-semibold text-[#EAECEF] mb-4 flex items-center gap-2">
</h3>
<div className="space-y-3">
<InfoRow
label="保证金模式"
value={traderData.is_cross_margin ? '全仓' : '逐仓'}
/>
<InfoRow
label="BTC/ETH 杠杆"
value={`${traderData.btc_eth_leverage}x`}
/>
<InfoRow
label="山寨币杠杆"
value={`${traderData.altcoin_leverage}x`}
/>
<InfoRow
label="交易币种"
value={traderData.trading_symbols || '使用默认币种'}
copyable
fieldName="trading_symbols"
label="扫描间隔"
value={`${traderData.scan_interval_minutes || 3} 分钟`}
/>
</div>
</div>
{/* Signal Sources */}
<div className="bg-[#0B0E11] border border-[#2B3139] rounded-lg p-5">
<h3 className="text-lg font-semibold text-[#EAECEF] mb-4 flex items-center gap-2">
📡
</h3>
<div className="space-y-3">
<InfoRow
label="Coin Pool 信号"
value={traderData.use_coin_pool}
/>
<InfoRow label="OI Top 信号" value={traderData.use_oi_top} />
</div>
</div>
{/* Custom Prompt */}
<div className="bg-[#0B0E11] border border-[#2B3139] rounded-lg p-5">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-[#EAECEF] flex items-center gap-2">
💬
{/* Strategy Info - only show if strategy is bound */}
{traderData.strategy_id && (
<div className="bg-[#0B0E11] border border-[#2B3139] rounded-lg p-5">
<h3 className="text-lg font-semibold text-[#EAECEF] mb-4 flex items-center gap-2">
📋 使
</h3>
{traderData.custom_prompt && (
<CopyButton
text={traderData.custom_prompt}
fieldName="custom_prompt"
<div className="space-y-3">
<InfoRow
label="策略名称"
value={traderData.strategy_name || traderData.strategy_id}
/>
)}
</div>
</div>
<div className="space-y-3">
<InfoRow
label="覆盖默认提示词"
value={traderData.override_base_prompt}
/>
{traderData.custom_prompt ? (
<div>
<div className="text-sm text-[#848E9C] mb-2">
{traderData.override_base_prompt
? '自定义提示词'
: '附加提示词'}
</div>
<div
className="p-3 rounded border text-sm text-[#EAECEF] font-mono leading-relaxed max-h-48 overflow-y-auto"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
whiteSpace: 'pre-wrap',
}}
>
{traderData.custom_prompt}
</div>
</div>
) : (
<div
className="text-sm text-[#848E9C] italic p-3 rounded border"
style={{ border: '1px solid #2B3139' }}
>
使
</div>
)}
</div>
</div>
)}
</div>
{/* Footer */}
<div className="flex justify-end gap-3 p-6 border-t border-[#2B3139] bg-gradient-to-r from-[#1E2329] to-[#252B35]">
<div className="flex justify-end p-6 border-t border-[#2B3139] bg-gradient-to-r from-[#1E2329] to-[#252B35]">
<button
onClick={onClose}
className="px-6 py-3 bg-[#2B3139] text-[#EAECEF] rounded-lg hover:bg-[#404750] transition-all duration-200 border border-[#404750]"
>
</button>
<button
onClick={() =>
copyToClipboard(
JSON.stringify(traderData, null, 2),
'full_config'
)
}
className="px-6 py-3 bg-gradient-to-r from-[#F0B90B] to-[#E1A706] text-black rounded-lg hover:from-[#E1A706] hover:to-[#D4951E] transition-all duration-200 font-medium shadow-lg"
>
{copiedField === 'full_config' ? '✓ 已复制配置' : '📋 复制完整配置'}
</button>
</div>
</div>
</div>