Dev backtest (#1134)

This commit is contained in:
Rick
2025-11-28 21:34:27 +08:00
committed by GitHub
parent 04f006abad
commit 11b6c6ba3e
39 changed files with 9293 additions and 125 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,177 @@
import { useState } from 'react'
import type { DecisionRecord } from '../types'
import { t, type Language } from '../i18n/translations'
interface DecisionCardProps {
decision: DecisionRecord
language: Language
}
export function DecisionCard({ decision, language }: DecisionCardProps) {
const [showInputPrompt, setShowInputPrompt] = useState(false)
const [showCoT, setShowCoT] = useState(false)
return (
<div
className="rounded p-5 transition-all duration-300 hover:translate-y-[-2px]"
style={{
border: '1px solid #2B3139',
background: '#1E2329',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
}}
>
<div className="flex items-start justify-between mb-3">
<div>
<div className="font-semibold" style={{ color: '#EAECEF' }}>
{t('cycle', language)} #{decision.cycle_number}
</div>
<div className="text-xs" style={{ color: '#848E9C' }}>
{new Date(decision.timestamp).toLocaleString()}
</div>
</div>
<div
className="px-3 py-1 rounded text-xs font-bold"
style={
decision.success
? { background: 'rgba(14, 203, 129, 0.1)', color: '#0ECB81' }
: { background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }
}
>
{t(decision.success ? 'success' : 'failed', language)}
</div>
</div>
{decision.input_prompt && (
<div className="mb-3">
<button
onClick={() => setShowInputPrompt(!showInputPrompt)}
className="flex items-center gap-2 text-sm transition-colors"
style={{ color: '#60a5fa' }}
>
<span className="font-semibold">
📥 {t('inputPrompt', language)}
</span>
<span className="text-xs">
{showInputPrompt ? t('collapse', language) : t('expand', language)}
</span>
</button>
{showInputPrompt && (
<div
className="mt-2 rounded p-4 text-sm font-mono whitespace-pre-wrap max-h-96 overflow-y-auto"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
>
{decision.input_prompt}
</div>
)}
</div>
)}
{decision.cot_trace && (
<div className="mb-3">
<button
onClick={() => setShowCoT(!showCoT)}
className="flex items-center gap-2 text-sm transition-colors"
style={{ color: '#F0B90B' }}
>
<span className="font-semibold">
📤 {t('aiThinking', language)}
</span>
<span className="text-xs">
{showCoT ? t('collapse', language) : t('expand', language)}
</span>
</button>
{showCoT && (
<div
className="mt-2 rounded p-4 text-sm font-mono whitespace-pre-wrap max-h-96 overflow-y-auto"
style={{
background: '#0B0E11',
border: '1px solid #2B3139',
color: '#EAECEF',
}}
>
{decision.cot_trace}
</div>
)}
</div>
)}
{decision.decisions && decision.decisions.length > 0 && (
<div className="space-y-2 mb-3">
{decision.decisions.map((action, index) => (
<div
key={`${action.symbol}-${index}`}
className="flex items-center gap-2 text-sm rounded px-3 py-2"
style={{ background: '#0B0E11' }}
>
<span
className="font-mono font-bold"
style={{ color: '#EAECEF' }}
>
{action.symbol}
</span>
<span
className="px-2 py-0.5 rounded text-xs font-bold"
style={
action.action.includes('open')
? {
background: 'rgba(96, 165, 250, 0.1)',
color: '#60a5fa',
}
: action.action.includes('close')
? {
background: 'rgba(14, 203, 129, 0.1)',
color: '#0ECB81',
}
: {
background: 'rgba(248, 113, 113, 0.1)',
color: '#F87171',
}
}
>
{action.action}
</span>
{action.reasoning && (
<span
className="text-xs"
style={{ color: '#848E9C', flex: 1 }}
>
{action.reasoning}
</span>
)}
</div>
))}
</div>
)}
{decision.execution_log && decision.execution_log.length > 0 && (
<div
className="rounded p-3 text-xs font-mono space-y-1"
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
>
{decision.execution_log.map((log, index) => (
<div key={`${log}-${index}`} style={{ color: '#EAECEF' }}>
{log}
</div>
))}
</div>
)}
{decision.error_message && (
<div
className="rounded p-3 mt-3 text-sm"
style={{
background: 'rgba(246, 70, 93, 0.1)',
border: '1px solid rgba(246, 70, 93, 0.4)',
color: '#F6465D',
}}
>
{decision.error_message}
</div>
)}
</div>
)
}

View File

@@ -6,16 +6,25 @@ import { t, type Language } from '../i18n/translations'
import { Container } from './Container'
import { useSystemConfig } from '../hooks/useSystemConfig'
type Page =
| 'competition'
| 'traders'
| 'trader'
| 'backtest'
| 'faq'
| 'login'
| 'register'
interface HeaderBarProps {
onLoginClick?: () => void
isLoggedIn?: boolean
isHomePage?: boolean
currentPage?: string
currentPage?: Page
language?: Language
onLanguageChange?: (lang: Language) => void
user?: { email: string } | null
onLogout?: () => void
onPageChange?: (page: string) => void
onPageChange?: (page: Page) => void
}
export default function HeaderBar({
@@ -207,6 +216,47 @@ export default function HeaderBar({
{t('dashboardNav', language)}
</button>
<button
onClick={() => {
if (onPageChange) {
onPageChange('backtest')
}
navigate('/backtest')
}}
className="text-sm font-bold transition-all duration-300 relative focus:outline-2 focus:outline-yellow-500"
style={{
color:
currentPage === 'backtest'
? 'var(--brand-yellow)'
: 'var(--brand-light-gray)',
padding: '8px 16px',
borderRadius: '8px',
position: 'relative',
}}
onMouseEnter={(e) => {
if (currentPage !== 'backtest') {
e.currentTarget.style.color = 'var(--brand-yellow)'
}
}}
onMouseLeave={(e) => {
if (currentPage !== 'backtest') {
e.currentTarget.style.color = 'var(--brand-light-gray)'
}
}}
>
{currentPage === 'backtest' && (
<span
className="absolute inset-0 rounded-lg"
style={{
background: 'rgba(240, 185, 11, 0.15)',
zIndex: -1,
}}
/>
)}
Backtest
</button>
<button
onClick={() => {
if (onPageChange) {