确认PokieTicker新闻库数据源

This commit is contained in:
2026-03-16 02:19:25 +08:00
parent 78f133617f
commit 564c92c0c8
182 changed files with 6436 additions and 1050 deletions

View File

@@ -1,294 +0,0 @@
import React, { useState } from 'react';
import Header from './Header.jsx';
export default function AboutModal({ onClose }) {
const [isClosing, setIsClosing] = useState(false);
const [language] = useState('zh');
const handleClose = () => {
setIsClosing(true);
// Wait for animation to complete before actually closing
setTimeout(() => {
onClose();
}, 600); // Match animation duration
};
const overlayStyle = {
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: '#ffffff',
zIndex: 9999,
animation: isClosing
? 'collapseUp 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards'
: 'expandDown 0.6s cubic-bezier(0.4, 0, 0.2, 1)',
transformOrigin: 'top center',
overflowY: 'auto'
};
const contentStyle = {
maxWidth: '900px',
width: '90%',
margin: '0 auto',
textAlign: 'left',
fontFamily: "'IBM Plex Mono', monospace",
color: '#000000',
lineHeight: 1.8,
fontSize: '14px',
letterSpacing: '0.01em',
padding: '60px 20px 80px',
animation: isClosing
? 'fadeOutContent 0.4s ease forwards'
: 'fadeInContent 0.8s ease 0.3s backwards'
};
const highlight = {
color: '#615CED',
fontWeight: 600
};
const linkStyle = {
color: '#615CED',
textDecoration: 'none',
borderBottom: '1px solid #615CED',
transition: 'all 0.2s'
};
const closeHintStyle = {
marginTop: '50px',
fontSize: '11px',
color: '#999',
cursor: 'pointer',
textAlign: 'center'
};
const languageSwitchStyle = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginBottom: '25px',
marginTop: '10px',
gap: '0px',
fontSize: '11px',
fontFamily: "'IBM Plex Mono', monospace"
};
const getLangStyle = (isActive) => ({
padding: '3px 8px',
cursor: 'pointer',
transition: 'all 0.2s',
background: isActive ? '#000' : '#fff',
color: isActive ? '#fff' : '#000',
border: 'none'
});
const content = {
en: {
question: "What happens if AI models don't compete with each other, but instead trade like a ",
questionHighlight: "well-coordinated, high-performance team",
questionEnd: "?",
intro: "Not arena, but TEAM. We Hope that AI is no longer entering the financial markets as isolated models—it is stepping in as ",
introHighlight1: "teams",
introContinue: ", collaborating in one of the most challenging and noise-filled ",
introHighlight2: "real-time environments",
introContinue2: ".",
point1Highlight: "✦ Complementary skills",
point1: " - across multiple agents—data analysis, strategy generation, risk management—working together like a real trading desk, exchanging information through notifications and meetings.",
point2Highlight: "✦ An agent system that continually evolves",
point2: " — with memory modules that retain experience, learn from market feedback, reflect, and develop their own methodology over time.",
point3Highlight: "✦ AI teams interacting with live markets",
point3: " — learning from real-time data and making immediate decisions, not just theoretical simulations."
},
zh: {
intro: "如果不是让模型彼此竞争,而是像一支高效协作的团队一样进行实时交易,会发生什么?",
question: "这里不是竞技场而是团队。我们希望Agents不再单打独斗而是「组团」进入实时金融市场——这一十分困难且充满噪声的环境。",
trying: "我们正在探索多智能体协作在实时金融交易中的可能性。",
title1: "✦ 多智能体的技能互补",
point1: "不同模型、不同角色的智能体像真实的金融团队一样协作,各自承担数据分析、策略生成、风险控制等职责。",
point1Sub: "通过通知和会议机制进行信息交换,实现高效协作。",
title2: "✦ 能够持续进化的智能体系统",
point2: "依托「记忆」模块每个智能体都能跨回合保留经验不断学习、反思与调整。我们希望能看到在长期实时交易中Agent形成自己的独特方法论而不是一次性偶然的推理。",
point2Sub: "ReMe 记忆框架帮助 Agents 持续改进。",
title3: "✦ 实时参与市场的 AI Agents",
point3: "Agents从实时行情中学习并给予即时决策不是纸上谈兵而是面对市场的真实波动。"
}
};
return (
<>
<style>{`
@keyframes expandDown {
from {
transform: scaleY(0);
opacity: 0;
}
to {
transform: scaleY(1);
opacity: 1;
}
}
@keyframes collapseUp {
from {
transform: scaleY(1);
opacity: 1;
}
to {
transform: scaleY(0);
opacity: 0;
}
}
@keyframes fadeInContent {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeOutContent {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-20px);
}
}
`}</style>
<div style={overlayStyle} onClick={handleClose}>
{/* Header */}
<div className="header" style={{
animation: isClosing
? 'fadeOutContent 0.4s ease forwards'
: 'fadeInContent 0.8s ease 0.3s backwards'
}} onClick={(e) => e.stopPropagation()}>
<Header
onEvoTradersClick={handleClose}
evoTradersLinkStyle="close"
/>
</div>
{/* Content */}
<div style={contentStyle} onClick={(e) => e.stopPropagation()}>
<div style={languageSwitchStyle}>
<span
style={getLangStyle(true)}
>
中文
</span>
</div>
// Chinese Content
<>
<div style={{ marginBottom: '30px' }}>
{content.zh.intro}
</div>
<div style={{ marginBottom: '40px', fontSize: '15px', fontWeight: 600 }}>
{content.zh.question}
</div>
<div style={{ marginBottom: '30px', fontSize: '14px', opacity: 0.8 }}>
{content.zh.trying}
</div>
<div style={{ marginBottom: '30px' }}>
<div style={{ ...highlight, marginBottom: '10px' }}>
{content.zh.title1}
</div>
<div style={{ marginBottom: '10px' }}>
{content.zh.point1}
</div>
<div style={{ fontSize: '13px', opacity: 0.7 }}>
{content.zh.point1Sub}
</div>
</div>
<div style={{ marginBottom: '30px' }}>
<div style={{ ...highlight, marginBottom: '10px' }}>
{content.zh.title2}
</div>
<div style={{ marginBottom: '10px' }}>
{content.zh.point2}
</div>
<div style={{ fontSize: '13px', opacity: 0.7 }}>
{content.zh.point2Sub}
</div>
</div>
<div style={{ marginBottom: '30px' }}>
<div style={{ ...highlight, marginBottom: '10px' }}>
{content.zh.title3}
</div>
<div>
{content.zh.point3}
</div>
</div>
<div style={{ marginBottom: '10px', opacity: 0.7 }}>
我们已经在 GitHub 上开源
</div>
<div style={{ marginBottom: '25px', opacity: 0.7 }}>
EvoTraders 基于{' '}
<a
href="https://github.com/agentscope-ai"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
AgentScope
</a>
{' '}搭建并使用其中的{' '}
<a
href="https://github.com/agentscope-ai/ReMe"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
ReMe
</a>
{' '}作为记忆管理核心
</div>
<div style={{ marginBottom: '10px', fontSize: '14px' }}>
你可以在此找到完整项目与示例
</div>
</>
<div style={{ marginTop: '40px' }}>
<a
href="https://github.com/agentscope-ai/agentscope-samples"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
github.com/agentscope-ai/agentscope-samples
</a>
</div>
<div style={closeHintStyle} onClick={handleClose}>
点击此处关闭
</div>
</div>
</div>
</>
);
}

View File

@@ -149,7 +149,7 @@ const AgentFeed = forwardRef(({ feed, leaderboard }, ref) => {
// Get current selection display info
const getCurrentSelectionInfo = () => {
if (selectedAgent === 'all') {
return { label: 'All Agents', modelInfo: null };
return { label: '全部角色', modelInfo: null };
}
const agentInfo = getAgentInfoByName(selectedAgent);
const modelInfo = agentInfo ? getModelIcon(agentInfo.modelName, agentInfo.modelProvider) : null;
@@ -191,7 +191,7 @@ const AgentFeed = forwardRef(({ feed, leaderboard }, ref) => {
setDropdownOpen(false);
}}
>
<span>全部 Agents</span>
<span>全部角色</span>
</div>
{uniqueAgents.map(agent => {
const agentInfo = getAgentInfoByName(agent);
@@ -419,17 +419,14 @@ function MemoryItem({ memory, itemId, isHighlighted }) {
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
>
<a
href="https://github.com/agentscope-ai/ReMe"
target="_blank"
rel="noopener noreferrer"
<span
style={{ display: 'flex', alignItems: 'center', textDecoration: 'none' }}
>
<img
src={ASSETS.remeLogo}
alt="ReMe"
alt="Memory"
style={{
cursor: 'pointer',
cursor: 'default',
height: '12px',
width: 'auto',
objectFit: 'contain',
@@ -449,9 +446,9 @@ function MemoryItem({ memory, itemId, isHighlighted }) {
lineHeight: 1,
pointerEvents: 'none'
}}>
MEMORY
</span>
</a>
</span>
</div>
<span style={{
background: 'linear-gradient(90deg, #00C2FF 0%, #5C4CE0 100%)',
@@ -497,10 +494,10 @@ function MemoryItem({ memory, itemId, isHighlighted }) {
color: 'transparent',
display: 'inline-block'
}}>
Memory powered by AgentScope-ReMe
Runtime Memory Layer
</div>
<div style={{ color: '#475569', opacity: 0.9 }}>
Not only retrieves historical memories but also generates suggestions and hints for the current task based on latest context.
Retrieves relevant historical context and produces guidance for the current task based on the latest conversation state.
</div>
</div>

View File

@@ -1,253 +1,29 @@
import React, { useState } from 'react';
import React from 'react';
/**
* Header Component
* Reusable header brand with EvoTraders logo, GitHub link, and Contact Us section
*
* @param {Function} onEvoTradersClick - Optional callback when EvoTraders is clicked
* @param {string} evoTradersLinkStyle - Optional style variant: 'default' | 'close'
* Reusable header brand for EvoTraders.
*/
export default function Header({
onEvoTradersClick = null,
evoTradersLinkStyle = 'default' // 'default' shows ↗, 'close' shows ↙
}) {
const [activeContactCard, setActiveContactCard] = useState({ yue: false, jiaji: false });
const [clickedContactCard, setClickedContactCard] = useState(null);
const handleEvoTradersClick = () => {
if (onEvoTradersClick) {
onEvoTradersClick();
}
};
export default function Header() {
return (
<div className="header-title" style={{ flex: '0 1 auto', minWidth: 0 }}>
<span
className="header-link"
onClick={handleEvoTradersClick}
style={{ cursor: 'pointer', padding: '4px 8px', borderRadius: '3px', display: 'inline-flex', alignItems: 'center', gap: '8px' }}
style={{
padding: '4px 8px',
borderRadius: '3px',
display: 'inline-flex',
alignItems: 'center',
gap: '8px'
}}
>
<img
src="/trading_logo.png"
alt="EvoTraders Logo"
style={{ height: '24px', width: 'auto' }}
/>
EvoTraders {evoTradersLinkStyle === 'close' ? (
<span className="link-arrow"></span>
) : (
<span className="link-arrow"></span>
)}
EvoTraders
</span>
<span style={{
width: '2px',
height: '16px',
background: '#666',
margin: '0 16px',
display: 'inline-block',
verticalAlign: 'middle'
}} />
<span style={{
padding: '1px 5px',
fontSize: '9px',
fontWeight: 700,
color: '#00C853',
background: 'rgba(0, 200, 83, 0.1)',
border: '1px solid #00C853',
borderRadius: '3px',
letterSpacing: '0.5px',
marginRight: '0px'
}}>
开源
</span>
<a
href="https://github.com/agentscope-ai/agentscope-samples"
target="_blank"
rel="noopener noreferrer"
className="header-link"
style={{ display: 'inline-flex', flexDirection: 'row', alignItems: 'center', gap: '6px' }}
>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="currentColor"
style={{ display: 'inline-block' }}
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
<span>agentscope-samples</span>
<span className="link-arrow"></span>
</a>
<a
href="https://github.com/agentscope-ai/ReMe"
target="_blank"
rel="noopener noreferrer"
className="header-link"
style={{ display: 'inline-flex', flexDirection: 'row', alignItems: 'center', gap: '6px', marginLeft: '0px' }}
>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="currentColor"
style={{ display: 'inline-block' }}
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
<span>agentscope-ReMe</span>
<span className="link-arrow"></span>
</a>
<span style={{
width: '2px',
height: '16px',
background: '#666',
margin: '0 16px',
display: 'inline-block',
verticalAlign: 'middle'
}} />
<div
style={{
position: 'relative',
display: 'inline-flex',
alignItems: 'center',
gap: '8px',
cursor: 'pointer'
}}
onClick={() => {
const bothActive = activeContactCard.yue && activeContactCard.jiaji;
if (!bothActive) {
setActiveContactCard({ yue: true, jiaji: true });
setClickedContactCard('both');
} else {
setActiveContactCard({ yue: false, jiaji: false });
setClickedContactCard(null);
}
}}
>
<span className="header-link">
联系我们
</span>
{/* Two contact buttons */}
<div style={{ display: 'flex', gap: '6px', alignItems: 'center' }}>
<div
onClick={(e) => {
e.stopPropagation();
if (activeContactCard.yue) {
setActiveContactCard(prev => ({ ...prev, yue: false }));
if (clickedContactCard === 'yue' || clickedContactCard === 'both') {
setClickedContactCard(null);
}
} else {
setActiveContactCard(prev => ({ ...prev, yue: true }));
setClickedContactCard('yue');
}
}}
onMouseEnter={() => {
if (!clickedContactCard || clickedContactCard === 'yue' || clickedContactCard === 'both') {
setActiveContactCard(prev => ({ ...prev, yue: true }));
}
}}
onMouseLeave={() => {
if (clickedContactCard !== 'yue' && clickedContactCard !== 'both') {
setActiveContactCard(prev => ({ ...prev, yue: false }));
}
}}
style={{
padding: '4px 8px',
background: activeContactCard.yue ? '#615CED' : '#f5f5f5',
color: activeContactCard.yue ? '#fff' : '#333',
border: '1px solid',
borderColor: activeContactCard.yue ? '#615CED' : '#e0e0e0',
borderRadius: '3px',
fontSize: '10px',
fontWeight: 700,
fontFamily: "'IBM Plex Mono', monospace",
cursor: 'pointer',
transition: 'all 0.2s',
letterSpacing: '0.5px',
whiteSpace: 'nowrap',
overflow: 'hidden',
maxWidth: activeContactCard.yue ? '80px' : '32px',
minWidth: activeContactCard.yue ? '80px' : '32px'
}}
>
{activeContactCard.yue ? (
<a
href="https://1mycell.github.io/"
target="_blank"
rel="noopener noreferrer"
style={{ color: 'inherit', textDecoration: 'none' }}
onClick={(e) => e.stopPropagation()}
>
Yue Wu
</a>
) : 'YW'}
</div>
<div
onClick={(e) => {
e.stopPropagation();
if (activeContactCard.jiaji) {
setActiveContactCard(prev => ({ ...prev, jiaji: false }));
if (clickedContactCard === 'jiaji' || clickedContactCard === 'both') {
setClickedContactCard(null);
}
} else {
setActiveContactCard(prev => ({ ...prev, jiaji: true }));
setClickedContactCard('jiaji');
}
}}
onMouseEnter={() => {
if (!clickedContactCard || clickedContactCard === 'jiaji' || clickedContactCard === 'both') {
setActiveContactCard(prev => ({ ...prev, jiaji: true }));
}
}}
onMouseLeave={() => {
if (clickedContactCard !== 'jiaji' && clickedContactCard !== 'both') {
setActiveContactCard(prev => ({ ...prev, jiaji: false }));
}
}}
style={{
padding: '4px 8px',
background: activeContactCard.jiaji ? '#615CED' : '#f5f5f5',
color: activeContactCard.jiaji ? '#fff' : '#333',
border: '1px solid',
borderColor: activeContactCard.jiaji ? '#615CED' : '#e0e0e0',
borderRadius: '3px',
fontSize: '10px',
fontWeight: 700,
fontFamily: "'IBM Plex Mono', monospace",
cursor: 'pointer',
transition: 'all 0.2s',
letterSpacing: '0.5px',
whiteSpace: 'nowrap',
overflow: 'hidden',
maxWidth: activeContactCard.jiaji ? '100px' : '32px',
minWidth: activeContactCard.jiaji ? '100px' : '32px'
}}
>
{activeContactCard.jiaji ? (
<a
href="https://dengjiaji.github.io/self/"
target="_blank"
rel="noopener noreferrer"
style={{ color: 'inherit', textDecoration: 'none' }}
onClick={(e) => e.stopPropagation()}
>
Jiaji Deng
</a>
) : 'JD'}
</div>
</div>
</div>
</div>
);
}

View File

@@ -554,7 +554,7 @@ export default function NetValueChart({ equity, baseline, baseline_vw, momentum,
fontFamily: '"Courier New", monospace',
fontSize: '12px'
}}>
NO DATA AVAILABLE
暂无图表数据
</div>
);
}
@@ -828,4 +828,3 @@ export default function NetValueChart({ equity, baseline, baseline_vw, momentum,
</ResponsiveContainer>
);
}

View File

@@ -1,360 +0,0 @@
import React, { useState, useEffect, useRef } from 'react';
import { LLM_MODEL_LOGOS } from '../config/constants';
export default function RulesView() {
const [language] = useState('zh');
const [scale, setScale] = useState(1);
const containerRef = useRef(null);
const contentRef = useRef(null);
// Auto-scale content to fit container without scrolling
useEffect(() => {
const handleResize = () => {
if (containerRef.current && contentRef.current) {
const containerHeight = containerRef.current.clientHeight;
const contentHeight = contentRef.current.scrollHeight;
if (contentHeight > containerHeight) {
const newScale = containerHeight / contentHeight;
setScale(Math.max(newScale * 0.95, 0.5)); // Min scale 0.5, with 95% of available space
} else {
setScale(1);
}
}
};
// Initial resize
handleResize();
// Listen to window resize
window.addEventListener('resize', handleResize);
// Observe content changes
const observer = new ResizeObserver(handleResize);
if (contentRef.current) {
observer.observe(contentRef.current);
}
return () => {
window.removeEventListener('resize', handleResize);
observer.disconnect();
};
}, [language]);
const containerStyle = {
width: '100%',
height: '100%',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#FFFFFF',
padding: '10px'
};
const contentWrapperStyle = {
transform: `scale(${scale})`,
transformOrigin: 'center center',
transition: 'transform 0.3s ease',
width: '100%',
maxWidth: '900px'
};
const innerContentStyle = {
color: '#000000',
fontFamily: "'IBM Plex Mono', monospace",
fontSize: '13px',
lineHeight: '1.6',
letterSpacing: '0.01em',
padding: '0 10px'
};
const highlight = {
color: '#000000',
fontWeight: 700
};
const sectionTitleStyle = {
color: '#615CED',
fontSize: '16px',
fontWeight: 700,
marginBottom: '8px',
marginTop: '12px',
marginLeft: '-10px',
marginRight: '-10px',
width: 'calc(100% + 20px)',
padding: '8px 10px',
backgroundColor: '#FFFFFF',
letterSpacing: '0.5px',
boxSizing: 'border-box'
};
const subsectionStyle = {
marginBottom: '8px',
paddingLeft: '10px',
borderLeft: '2px solid #CCCCCC'
};
const linkStyle = {
color: '#615CED',
textDecoration: 'none',
borderBottom: '1px solid #615CED',
transition: 'all 0.2s'
};
const languageSwitchStyle = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginBottom: '12px',
gap: '0px',
fontSize: '11px',
fontFamily: "'IBM Plex Mono', monospace"
};
const getLangStyle = (isActive) => ({
padding: '4px 10px',
cursor: 'pointer',
transition: 'all 0.2s',
background: isActive ? '#000000' : 'transparent',
color: isActive ? '#FFFFFF' : '#666666',
border: 'none',
borderRadius: '2px'
});
const llmLogos = [
{ name: 'Alibaba', file: 'Alibaba.jpeg', label: 'Qwen', url: LLM_MODEL_LOGOS['Alibaba'] },
{ name: 'DeepSeek', file: 'DeepSeek.png', label: 'DeepSeek', url: LLM_MODEL_LOGOS['DeepSeek'] },
{ name: 'Moonshot', file: 'Moonshot.jpeg', label: 'Moonshot', url: LLM_MODEL_LOGOS['Moonshot'] },
{ name: 'Zhipu AI', file: 'Zhipu AI.png', label: 'Zhipu AI', url: LLM_MODEL_LOGOS['Zhipu AI'] }
];
const content = {
en: {
section1Title: "Agent Setup",
pmRole: "Portfolio Manager",
pmDesc: "Makes final trading decisions and orchestrates team collaboration",
rmRole: "Risk Manager",
rmDesc: "Monitors portfolio risk and enforces risk limits",
analystsRole: "Analysts",
analystsDesc: "Conduct specialized research with different tools and AI models:",
analysts: [
{ name: "Valuation Analyst", model: "Moonshot", modelKey: "Moonshot" },
{ name: "Sentiment Analyst", model: "Qwen", modelKey: "Alibaba" },
{ name: "Fundamentals Analyst", model: "DeepSeek", modelKey: "DeepSeek" },
{ name: "Technical Analyst", model: "Zhipu AI", modelKey: "Zhipu AI" }
],
section2Title: "Agent Decision Mechanism",
tradingProcess: "Daily Trading Process",
tradingDesc: "Agents trade on a daily frequency while continuously tracking portfolio performance. Before each day's final trading decision, agents go through three key phases:",
analysisPhase: "• Analysis Phase",
analysisDesc: "All agents independently analyze information and form judgments based on their specialized tools.",
communicationPhase: "• Communication Phase",
commIntro: "Multiple communication channels enable effective collaboration: 1v1 Private Chat / 1vN Notification / NvN Conference",
decisionPhase: "• Decision Phase",
decisionDesc: "Portfolio Manager aggregates all information and makes the final team trading decision. The original trading signals from analysts are only used for individual-level ranking.",
reflectionTitle: "Learning & Evolution",
reflectionDesc: "Agents reflect on daily investment performance, summarize insights, and store them in ",
remeLink: "ReMe",
reflectionDesc2: " memory framework for continuous improvement.",
section3Title: "Performance Evaluation",
chartTitle: "• Performance Chart",
chartDesc: "Track portfolio equity curve vs. benchmarks (equal-weight, value-weighted, momentum). Use this to assess overall strategy effectiveness.",
rankingTitle: "• Analyst Rankings",
rankingDesc: "Click avatars in Trading Room to view analyst performance (Win Rate, Bull/Bear Win Rate). Use this to understand which analysts provide the most valuable insights.",
statsTitle: "• Statistics",
statsDesc: "Detailed holdings and trade history. Use this for in-depth analysis of position management and execution quality.",
callToAction: "Fork on ",
repoLink: "GitHub",
callToAction2: " to customize!"
},
zh: {
section1Title: "Agent 设定",
pmRole: "投资经理",
pmDesc: "负责最终交易决策和团队协作",
rmRole: "风控经理",
rmDesc: "监控组合风险并执行风险限制",
analystsRole: "分析师",
analystsDesc: "使用不同工具和 AI 模型进行专业研究:",
analysts: [
{ name: "估值分析师", model: "Moonshot", modelKey: "Moonshot" },
{ name: "情绪分析师", model: "Qwen", modelKey: "Alibaba" },
{ name: "基本面分析师", model: "DeepSeek", modelKey: "DeepSeek" },
{ name: "技术分析师", model: "Zhipu AI", modelKey: "Zhipu AI" }
],
section2Title: "Agent 决策机制",
tradingProcess: "交易流程",
tradingDesc: "智能体以日频进行交易并持续跟踪组合净值。每天最终交易决策前,会经历三个关键阶段:",
analysisPhase: "• 分析阶段",
analysisDesc: "所有智能体根据各自的工具和信息独立分析并形成判断。",
communicationPhase: "• 沟通阶段",
commIntro: "提供了多种沟通渠道1v1 私聊 / 1vN 通知 / NvN 会议",
decisionPhase: "• 决策阶段",
decisionDesc: "由投资经理汇总所有信息,并给出最终的团队交易决策。分析师给出的原始交易信号仅用于个人维度排名。",
reflectionTitle: "学习与进化",
reflectionDesc: "智能体根据当日实际收益反思决策、总结经验,并存入 ",
remeLink: "ReMe",
reflectionDesc2: " 记忆框架以持续改进。",
section3Title: "收益评估",
chartTitle: "• 业绩图表",
chartDesc: "追踪组合收益曲线 vs. 基准策略(等权、市值加权、动量)。用于评估整体策略有效性。",
rankingTitle: "• 分析师排名",
rankingDesc: "在交易室点击头像查看分析师表现(胜率、牛/熊市胜率),用来了解哪些分析师提供了最有价值的洞察。",
statsTitle: "• 统计数据",
statsDesc: "详细的持仓和交易历史。用于深入分析仓位管理和执行质量。",
callToAction: "可在 ",
repoLink: "GitHub",
callToAction2: " 上 Fork 并自行定制。"
}
};
return (
<div ref={containerRef} style={containerStyle}>
<div ref={contentRef} style={contentWrapperStyle}>
<div style={innerContentStyle}>
<div style={languageSwitchStyle}>
<span
style={getLangStyle(true)}
>
中文
</span>
</div>
// Chinese Content
<>
{/* 第一部分Agent 设定 */}
<div style={sectionTitleStyle}>{content.zh.section1Title}</div>
{/* 角色 */}
<div style={{ marginBottom: '8px', fontSize: '12px' }}>
<div style={{ marginBottom: '3px' }}>
<span style={{ fontWeight: 600 }}>{content.zh.pmRole}:</span> {content.zh.pmDesc}
</div>
<div style={{ marginBottom: '3px' }}>
<span style={{ fontWeight: 600 }}>{content.zh.rmRole}:</span> {content.zh.rmDesc}
</div>
<div style={{ marginBottom: '3px' }}>
<span style={{ fontWeight: 600 }}>{content.zh.analystsRole}:</span> {content.zh.analystsDesc}
</div>
</div>
{/* Analysts 与 AI 模型 */}
<div style={{ marginLeft: '10px', marginBottom: '8px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '3px 14px', fontSize: '11px' }}>
{content.zh.analysts.map(analyst => {
const logo = llmLogos.find(l => l.name === analyst.modelKey);
return (
<div key={analyst.name} style={{
display: 'flex',
alignItems: 'center',
gap: '8px'
}}>
{logo && (
<img
src={logo.url}
alt={logo.label}
style={{
height: '16px',
width: 'auto',
objectFit: 'contain'
}}
/>
)}
<span style={{ fontWeight: 600 }}>{analyst.name}</span>
<span style={{ color: '#666' }}>- {analyst.model}</span>
</div>
);
})}
</div>
<div style={{ marginBottom: '10px', fontSize: '11px', fontStyle: 'italic', opacity: 0.8 }}>
{content.zh.callToAction}
<a
href="https://github.com/agentscope-ai/agentscope-samples"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
{content.zh.repoLink}
</a>
{content.zh.callToAction2}
</div>
{/* 第二部分Agent 决策机制 */}
<div style={sectionTitleStyle}>{content.zh.section2Title}</div>
<div style={{ marginBottom: '6px' }}>
<div style={{ fontWeight: 600, marginBottom: '3px' }}>{content.zh.tradingProcess}</div>
<div style={{ marginBottom: '6px', fontSize: '12px' }}>{content.zh.tradingDesc}</div>
<div style={subsectionStyle}>
<div style={{ marginBottom: '4px', fontSize: '12px' }}>
<span style={highlight}>{content.zh.analysisPhase.replace('• ', '')}:</span> {content.zh.analysisDesc}
</div>
<div style={{ marginBottom: '4px', fontSize: '12px' }}>
<span style={highlight}>{content.zh.communicationPhase.replace('• ', '')}:</span> {content.zh.commIntro}
</div>
<div style={{ fontSize: '12px' }}>
<span style={highlight}>{content.zh.decisionPhase.replace('• ', '')}:</span> {content.zh.decisionDesc}
</div>
</div>
</div>
<div style={{ marginBottom: '10px' }}>
<div style={{ fontWeight: 600, marginBottom: '3px' }}>{content.zh.reflectionTitle}</div>
<div style={{ fontSize: '12px' }}>
{content.zh.reflectionDesc}
<a
href="https://github.com/agentscope-ai/ReMe"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
{content.zh.remeLink}
</a>
{content.zh.reflectionDesc2}
</div>
</div>
{/* 第三部分:收益评估 */}
<div style={sectionTitleStyle}>{content.zh.section3Title}</div>
<div style={subsectionStyle}>
<div style={{ marginBottom: '3px', fontSize: '12px' }}>
<span style={{ fontWeight: 600 }}>{content.zh.chartTitle.replace('• ', '')}:</span> {content.zh.chartDesc}
</div>
<div style={{ marginBottom: '3px', fontSize: '12px' }}>
<span style={{ fontWeight: 600 }}>{content.zh.rankingTitle.replace('• ', '')}:</span> {content.zh.rankingDesc}
</div>
<div style={{ fontSize: '12px' }}>
<span style={{ fontWeight: 600 }}>{content.zh.statsTitle.replace('• ', '')}:</span> {content.zh.statsDesc}
</div>
</div>
</>
</div>
</div>
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
import React from 'react';
export default function WatchlistPanel({
isOpen,
isConnected,
isSaving,
draftSymbols,
inputValue,
feedback,
suggestions,
onToggle,
onClose,
onInputChange,
onInputKeyDown,
onAdd,
onRemove,
onRestoreCurrent,
onRestoreDefault,
onSuggestionClick,
onSave
}) {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, position: 'relative' }}>
<button
onClick={onToggle}
style={{
padding: '6px 10px',
borderRadius: 4,
border: '1px solid #333333',
background: isOpen ? '#1E1E1E' : '#111111',
color: '#FFFFFF',
fontSize: '11px',
fontWeight: 700,
letterSpacing: '0.6px',
cursor: 'pointer',
whiteSpace: 'nowrap'
}}
>
WATCHLIST
</button>
{isOpen && (
<div style={{
position: 'absolute',
top: 'calc(100% + 10px)',
right: 0,
width: 360,
maxWidth: 'min(360px, 92vw)',
padding: '14px',
borderRadius: 8,
border: '1px solid #D9D9D9',
background: '#FFFFFF',
boxShadow: '0 12px 36px rgba(0, 0, 0, 0.14)',
zIndex: 40,
display: 'flex',
flexDirection: 'column',
gap: 12
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 }}>
<div>
<div style={{ fontSize: '12px', fontWeight: 700, color: '#111111', letterSpacing: '0.3px' }}>
自选股管理
</div>
<div style={{ fontSize: '11px', color: '#666666', marginTop: 2 }}>
保存后会立即更新当前 run watchlist
</div>
</div>
<button
onClick={onClose}
style={{
border: 'none',
background: 'transparent',
color: '#666666',
cursor: 'pointer',
fontSize: '14px',
lineHeight: 1
}}
>
×
</button>
</div>
<div style={{
display: 'flex',
flexWrap: 'wrap',
gap: 8,
minHeight: 36,
padding: '2px 0'
}}>
{draftSymbols.map((symbol) => (
<button
key={symbol}
onClick={() => onRemove(symbol)}
style={{
display: 'inline-flex',
alignItems: 'center',
gap: 6,
padding: '6px 10px',
borderRadius: 999,
border: '1px solid #D0D7DE',
background: '#F7F9FB',
color: '#111111',
fontSize: '11px',
fontWeight: 700,
cursor: 'pointer'
}}
>
<span>{symbol}</span>
<span style={{ color: '#777777' }}>×</span>
</button>
))}
{draftSymbols.length === 0 && (
<div style={{ fontSize: '11px', color: '#888888', padding: '8px 2px' }}>
还没有股票输入代码后回车添加
</div>
)}
</div>
<div style={{ display: 'flex', gap: 8 }}>
<input
value={inputValue}
onChange={(e) => onInputChange(e.target.value)}
onKeyDown={onInputKeyDown}
placeholder="输入股票代码,回车添加"
style={{
flex: 1,
padding: '9px 10px',
borderRadius: 6,
border: '1px solid #D0D7DE',
background: '#FFFFFF',
color: '#111111',
fontSize: '12px',
fontFamily: '"Courier New", monospace'
}}
/>
<button
onClick={onAdd}
style={{
padding: '9px 12px',
borderRadius: 6,
border: '1px solid #D0D7DE',
background: '#F7F9FB',
color: '#111111',
fontSize: '11px',
fontWeight: 700,
cursor: 'pointer'
}}
>
添加
</button>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
{suggestions.map((symbol) => {
const active = draftSymbols.includes(symbol);
return (
<button
key={symbol}
onClick={() => onSuggestionClick(symbol)}
disabled={active}
style={{
padding: '5px 8px',
borderRadius: 999,
border: '1px solid',
borderColor: active ? '#B6E3C5' : '#D0D7DE',
background: active ? '#ECFDF3' : '#FFFFFF',
color: active ? '#157347' : '#4A5568',
fontSize: '10px',
fontWeight: 700,
cursor: active ? 'default' : 'pointer'
}}
>
{symbol}
</button>
);
})}
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<button
onClick={onRestoreCurrent}
style={{
padding: '8px 10px',
borderRadius: 6,
border: '1px solid #D0D7DE',
background: '#FFFFFF',
color: '#111111',
fontSize: '11px',
fontWeight: 700,
cursor: 'pointer'
}}
>
恢复当前
</button>
<button
onClick={onRestoreDefault}
style={{
padding: '8px 10px',
borderRadius: 6,
border: '1px solid #D0D7DE',
background: '#FFFFFF',
color: '#111111',
fontSize: '11px',
fontWeight: 700,
cursor: 'pointer'
}}
>
恢复默认
</button>
</div>
<button
onClick={onSave}
disabled={!isConnected || isSaving}
style={{
padding: '9px 14px',
borderRadius: 6,
border: '1px solid #1565C0',
background: isConnected && !isSaving ? '#0D47A1' : '#94A3B8',
color: '#FFFFFF',
fontSize: '11px',
fontWeight: 700,
letterSpacing: '0.4px',
cursor: isConnected && !isSaving ? 'pointer' : 'not-allowed'
}}
>
{isSaving ? '保存中' : '保存'}
</button>
</div>
{feedback && (
<span style={{
color: feedback.type === 'success' ? '#00C853' : '#FF5252',
fontSize: '11px',
fontFamily: '"Courier New", monospace'
}}>
{feedback.text}
</span>
)}
</div>
)}
</div>
);
}