mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-07 04:50:57 +08:00
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user