import { Alert, Button, Card, Col, Modal, Row, Space, Statistic, Table, Tag, message } from 'antd' import { useState } from 'react' import { apiService } from '../services/api' import type { CopyTradingStatistics } from '../types' import { formatUSDC } from '../utils' interface Props { statistics: CopyTradingStatistics onApplied?: () => void compact?: boolean } const fieldLabels: Record = { maxDailyOrders: '每日最大订单数', maxDailyLoss: '每日最大亏损', minPrice: '最低价格', maxPrice: '最高价格', maxPositionValue: '单市场最大仓位', minOrderDepth: '最小深度', maxSpread: '最大价差', priceTolerance: '价格容忍度' } const statusText: Record = { AVAILABLE: '报价可用', NO_MATCH: '有持仓未匹配到报价', UNAVAILABLE: '报价不可用' } const statusColor: Record = { AVAILABLE: 'green', NO_MATCH: 'orange', UNAVAILABLE: 'red' } type ApplyConservativeConfigPayload = Parameters[0] const CopyTradingRiskSeatbeltPanel: React.FC = ({ statistics, onApplied, compact = false }) => { const diagnosis = statistics.riskDiagnosis const [applying, setApplying] = useState(false) if (!diagnosis) { return ( ) } const riskWarnings = diagnosis.riskWarnings || [] const dangerousWarnings = riskWarnings.filter(item => item.severity === 'HIGH' || item.severity === 'MEDIUM') const buildApplyPayload = (): ApplyConservativeConfigPayload => { const payload: ApplyConservativeConfigPayload = { copyTradingId: statistics.copyTradingId, confirm: true } riskWarnings.forEach(item => { if (item.suggestedValue === undefined || item.suggestedValue === null) { return } switch (item.field) { case 'maxDailyOrders': payload.maxDailyOrders = Number(item.suggestedValue) break case 'maxDailyLoss': payload.maxDailyLoss = item.suggestedValue break case 'minPrice': payload.minPrice = item.suggestedValue break case 'maxPrice': payload.maxPrice = item.suggestedValue break case 'maxPositionValue': payload.maxPositionValue = item.suggestedValue break case 'minOrderDepth': payload.minOrderDepth = item.suggestedValue break case 'maxSpread': payload.maxSpread = item.suggestedValue break case 'priceTolerance': payload.priceTolerance = item.suggestedValue break } }) return payload } const applyConservativeConfig = () => { if (dangerousWarnings.length === 0) { message.info('当前配置已经比较保守,无需应用安全带建议') return } Modal.confirm({ title: '确认应用保守配置?', content: (

这只会修改风控白名单字段,不会启用/停用跟单,也不会更换 leader。

fieldLabels[field] || field }, { title: '当前值', dataIndex: 'currentValue', render: (value: string | null) => value ?? '未设置' }, { title: '建议值', dataIndex: 'suggestedValue' } ]} /> ), okText: '确认应用', cancelText: '取消', onOk: async () => { setApplying(true) try { const response = await apiService.safetyConfig.applyConservative(buildApplyPayload()) if (response.data.code === 0) { message.success('已应用保守配置') onApplied?.() } else { message.error(response.data.msg || '应用保守配置失败') } } finally { setApplying(false) } } }) } return ( {diagnosis.dataIncomplete && ( )} {diagnosis.lowConfidence && ( )}
报价状态
{statusText[diagnosis.quoteOverallStatus] || diagnosis.quoteOverallStatus}
可用 {diagnosis.quoteAvailableCount},未匹配 {diagnosis.quoteNoMatchCount},不可用 {diagnosis.quoteUnavailableCount}

亏损最大的市场

{diagnosis.topLosingMarkets.length === 0 ? ( ) : (
`$${formatUSDC(value)}` }, { title: '匹配数', dataIndex: 'matchedOrders' } ]} /> )}

风险配置体检

{riskWarnings.length === 0 ? ( ) : (
fieldLabels[field] || field }, { title: '当前值', dataIndex: 'currentValue', render: (value: string | null) => value ?? '未设置' }, { title: '建议值', dataIndex: 'suggestedValue' }, { title: '级别', dataIndex: 'severity', render: (severity: string) => {severity} }, { title: '原因', dataIndex: 'reason' } ]} /> )} ) } export default CopyTradingRiskSeatbeltPanel