Update: Merge nofx improvements

- Frontend trading records and UI enhancements
- Optimized AI prompts and decision engine
- Performance analysis and comparison features
- Binance-style UI improvements
This commit is contained in:
tinkle
2025-10-28 21:45:28 +08:00
parent afe513b7aa
commit 14ffb0593a
13 changed files with 1440 additions and 545 deletions

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ config.json
# 决策日志
decision_logs/
coin_pool_cache/
nofx-auto

655
README.md
View File

@@ -1,140 +1,136 @@
# 🤖 NOFX - AI-Driven Binance Futures Auto Trading Competition System
# 🤖 NOFX - AI驱动的币安合约自动交易竞赛系统
[![Go Version](https://img.shields.io/badge/Go-1.21+-00ADD8?style=flat&logo=go)](https://golang.org/)
[![React](https://img.shields.io/badge/React-18+-61DAFB?style=flat&logo=react)](https://reactjs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?style=flat&logo=typescript)](https://www.typescriptlang.org/)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
**Languages:** [English](README.md) | [中文](README.zh-CN.md) | [Українська](README.uk.md) | [Русский](README.ru.md)
一个基于 **DeepSeek/Qwen AI** 的币安合约自动交易系统,支持**多AI模型实盘竞赛**具备完整的市场分析、AI决策、**自我学习机制**和专业的Web监控界面。
> ⚠️ **风险提示**本系统为实验性项目AI自动交易存在重大风险强烈建议仅用于学习研究或小额资金测试
---
An automated Binance futures trading system powered by **DeepSeek/Qwen AI**, supporting **multi-AI model live trading competition**, featuring comprehensive market analysis, AI decision-making, **self-learning mechanism**, and professional Web monitoring interface.
## ✨ 核心特性
> ⚠️ **Risk Warning**: This system is experimental. AI auto-trading carries significant risks. Strongly recommended for learning/research purposes or testing with small amounts only!
### 🏆 多AI竞赛模式
- **Qwen vs DeepSeek** 实盘对抗
- 独立账户管理,独立决策日志
- 实时性能对比图表
- 收益率PK胜率统计
### 🧠 AI自我学习机制NEW
- **历史反馈**: 每次决策前分析最近20个周期的交易表现
- **智能优化**:
- 识别表现最佳/最差币种
- 计算胜率、盈亏比、平均盈利
- 避免重复错误(连续亏损的币种)
- 强化成功策略(高胜率的交易模式)
- **动态调整**: AI根据历史表现自主调整交易风格
### 📊 智能市场分析
- **3分钟K线**: 实时价格、EMA20、MACD、RSI(7)
- **4小时K线**: 长期趋势、EMA20/50、ATR、RSI(14)
- **持仓量分析**: 市场情绪、资金流向判断
- **OI Top追踪**: 持仓量增长最快的20个币种
- **AI500币种池**: 高评分币种自动筛选
- **流动性过滤**: 自动过滤持仓价值<15M USD的低流动性币种
### 🎯 专业风险控制
- **单币种仓位上限**:
- 山寨币 1.5倍账户净值
- BTC/ETH 10倍账户净值
- **固定杠杆**: 山寨币20倍 | BTC/ETH 50倍
- **保证金管理**: 总使用率90%AI自主决策使用率
- **风险回报比**: 强制1:2止损:止盈
- **防止仓位叠加**: 同币种同方向不允许重复开仓
### 🎨 风格UI
- **专业交易界面**: 视觉设计
- **暗色主题**: 经典配色金色#F0B90B + 深色背景
- **实时数据**: 5秒刷新账户持仓图表
- **收益率曲线**: 账户净值历史走势美元/百分比切换
- **性能对比图**: 多AI收益率实时对比
- **动画效果**: 流畅的hover过渡加载动画
### 📝 完整决策记录
- **思维链记录**: AI的完整推理过程CoT
- **历史表现**: 整体胜率平均盈利盈亏比
- **最近交易**: 最近5笔交易详情开仓价平仓价盈亏%
- **币种统计**: 各币种表现胜率平均盈亏
- **JSON日志**: 每次决策完整记录便于复盘分析
---
## ✨ Core Features
### 🏆 Multi-AI Competition Mode
- **Qwen vs DeepSeek** live trading battle
- Independent account management and decision logs
- Real-time performance comparison charts
- ROI PK and win rate statistics
### 🧠 AI Self-Learning Mechanism (NEW!)
- **Historical Feedback**: Analyzes last 20 cycles of trading performance before each decision
- **Smart Optimization**:
- Identifies best/worst performing coins
- Calculates win rate, profit/loss ratio, average profit
- Avoids repeating mistakes (consecutive losing coins)
- Reinforces successful strategies (high win rate patterns)
- **Dynamic Adjustment**: AI autonomously adjusts trading style based on historical performance
### 📊 Intelligent Market Analysis
- **3-minute K-line**: Real-time price, EMA20, MACD, RSI(7)
- **4-hour K-line**: Long-term trend, EMA20/50, ATR, RSI(14)
- **Open Interest Analysis**: Market sentiment, capital flow judgment
- **OI Top Tracking**: Top 20 coins with fastest growing open interest
- **AI500 Coin Pool**: Automatic high-score coin screening
- **Liquidity Filter**: Auto-filters low liquidity coins (<15M USD position value)
### 🎯 Professional Risk Control
- **Per-Coin Position Limit**:
- Altcoins 1.5x account equity
- BTC/ETH 10x account equity
- **Fixed Leverage**: Altcoins 20x | BTC/ETH 50x
- **Margin Management**: Total usage 90%, AI autonomous decision on usage rate
- **Risk-Reward Ratio**: Mandatory 1:2 (stop-loss:take-profit)
- **Prevent Position Stacking**: No duplicate opening of same coin/direction
### 🎨 Professional UI
- **Professional Trading Interface**: Binance-style visual design
- **Dark Theme**: Classic color scheme (Gold #F0B90B + dark background)
- **Real-time Data**: 5-second refresh for accounts, positions, charts
- **Equity Curve**: Historical account value trend (USD/percentage toggle)
- **Performance Comparison Chart**: Real-time multi-AI ROI comparison
- **Smooth Animations**: Fluid hover, transition, and loading effects
### 📝 Complete Decision Recording
- **Chain of Thought**: AI's complete reasoning process (CoT)
- **Historical Performance**: Overall win rate, average profit, profit/loss ratio
- **Recent Trades**: Last 5 trade details (entry price exit price P/L%)
- **Coin Statistics**: Per-coin performance (win rate, average P/L)
- **JSON Logs**: Complete decision records for post-trade analysis
---
## 🏗️ Technical Architecture
## 🏗️ 技术架构
```
nofx/
├── main.go # Program entry (multi-trader manager)
├── config.json # Configuration file (API keys, multi-trader config)
├── main.go # 程序入口多trader管理器
├── config.json # 配置文件API密钥、多trader配置
├── api/ # HTTP API service
│ └── server.go # Gin framework, RESTful API
├── api/ # HTTP API服务
│ └── server.go # Gin框架,RESTful API
├── trader/ # Trading core
│ ├── auto_trader.go # Auto trading main controller (single trader)
│ └── binance_futures.go # Binance futures API wrapper
├── trader/ # 交易核心
│ ├── auto_trader.go # 自动交易主控单trader
│ └── binance_futures.go # 币安合约API封装
├── manager/ # Multi-trader management
│ └── trader_manager.go # Manages multiple trader instances
├── manager/ # 多trader管理
│ └── trader_manager.go # 管理多个trader实例
├── market/ # Market data & AI decisions
│ ├── market_data.go # Market data fetching (K-line, indicators)
│ ├── ai_decision_engine.go # AI decision engine (with historical feedback)
│ └── ai_signal.go # AI API calls (DeepSeek/Qwen)
├── market/ # 市场数据与AI决策
│ ├── market_data.go # 市场数据获取K线、指标
│ ├── ai_decision_engine.go # AI决策引擎(含历史反馈)
│ └── ai_signal.go # AI API调用(DeepSeek/Qwen
├── pool/ # Coin pool management
│ └── coin_pool.go # AI500 + OI Top merged pool
├── pool/ # 币种池管理
│ └── coin_pool.go # AI500 + OI Top合并池
├── logger/ # Logging system
│ └── decision_logger.go # Decision recording + performance analysis
├── logger/ # 日志系统
│ └── decision_logger.go # 决策记录 + 表现分析
├── decision_logs/ # Decision log storage
│ ├── trader1/ # Trader 1 logs
│ └── trader2/ # Trader 2 logs
├── decision_logs/ # 决策日志存储
│ ├── trader1/ # Trader 1的日志
│ └── trader2/ # Trader 2的日志
└── web/ # React frontend
└── web/ # React前端
├── src/
│ ├── components/ # React components
│ │ ├── EquityChart.tsx # Equity curve chart
│ │ ├── ComparisonChart.tsx # Multi-AI comparison chart
│ │ └── CompetitionPage.tsx # Competition leaderboard
│ ├── lib/api.ts # API call wrapper
│ ├── types/index.ts # TypeScript types
│ ├── index.css # Binance-style CSS
│ └── App.tsx # Main app
│ ├── components/ # React组件
│ │ ├── EquityChart.tsx # 收益率曲线图
│ │ ├── ComparisonChart.tsx # 多AI对比图
│ │ └── CompetitionPage.tsx # 竞赛排行榜
│ ├── lib/api.ts # API调用封装
│ ├── types/index.ts # TypeScript类型
│ ├── index.css # Binance风格样式
│ └── App.tsx # 主应用
└── package.json
```
### Core Dependencies
### 核心依赖
**Backend (Go)**
- `github.com/adshao/go-binance/v2` - Binance API client
- `github.com/markcheno/go-talib` - Technical indicator calculation (TA-Lib)
- `github.com/gin-gonic/gin` - HTTP API framework
**后端 (Go)**
- `github.com/adshao/go-binance/v2` - 币安API客户端
- `github.com/markcheno/go-talib` - 技术指标计算TA-Lib
- `github.com/gin-gonic/gin` - HTTP API框架
**Frontend (React + TypeScript)**
- `react` + `react-dom` - UI framework
- `recharts` - Chart library (equity curve, comparison charts)
- `swr` - Data fetching and caching
- `tailwindcss` - CSS framework
**前端 (React + TypeScript)**
- `react` + `react-dom` - UI框架
- `recharts` - 图表库收益率曲线对比图
- `swr` - 数据获取和缓存
- `tailwindcss` - CSS框架
---
## 🚀 Quick Start
## 🚀 快速开始
### 1. Environment Requirements
### 1. 环境要求
- **Go 1.21+**
- **Node.js 18+**
- **TA-Lib** library (technical indicator calculation)
- **TA-Lib** 技术指标计算
#### Installing TA-Lib
#### 安装 TA-Lib
**macOS:**
```bash
@@ -146,32 +142,32 @@ brew install ta-lib
sudo apt-get install libta-lib0-dev
```
**Other systems**: Refer to [TA-Lib Official Documentation](https://github.com/markcheno/go-talib)
**其他系统**: 参考 [TA-Lib官方文档](https://github.com/markcheno/go-talib)
### 2. Clone the Project
### 2. 克隆项目
```bash
git clone https://github.com/tinkle-community/nofx.git
git clone <repository-url>
cd nofx
```
### 3. Install Dependencies
### 3. 安装依赖
**Backend:**
**后端:**
```bash
go mod download
```
**Frontend:**
**前端:**
```bash
cd web
npm install
cd ..
```
### 4. System Configuration
### 4. 配置系统
Create `config.json` file (use `config.json.example` as template):
创建 `config.json` 文件
```json
{
@@ -205,240 +201,319 @@ Create `config.json` file (use `config.json.example` as template):
}
```
**Configuration Notes:**
- `traders`: Configure 1-N traders (single AI or multi-AI competition)
- `id`: Unique trader identifier (used for log directory)
- `ai_model`: "qwen" or "deepseek"
- `binance_api_key/secret_key`: Each trader uses independent Binance account
- `initial_balance`: Initial balance (for calculating P/L%)
- `scan_interval_minutes`: Decision cycle (recommended 3-5 minutes)
- `coin_pool_api_url`: AI500 coin pool API (optional)
- `oi_top_api_url`: OI Top open interest API (optional)
**配置说明:**
- `traders`: 可配置1-N个trader单AI或多AI竞赛
- `id`: Trader唯一标识用于日志目录
- `ai_model`: "qwen" "deepseek"
- `binance_api_key/secret_key`: 每个trader使用独立的币安账户
- `initial_balance`: 初始余额用于计算盈亏%
- `scan_interval_minutes`: 决策周期建议3-5分钟
- `coin_pool_api_url`: AI500币种池API可选
- `oi_top_api_url`: OI Top持仓量API可选
### 5. Run the System
### 5. 运行系统
**Start backend (AI trading system + API server):**
**启动后端AI交易系统 + API服务器:**
```bash
go build -o nofx
./nofx
```
**Start frontend (Web Dashboard):**
**启动前端(Web Dashboard:**
Open a new terminal:
新开终端窗口
```bash
cd web
npm run dev
```
**Access the interface:**
**访问界面:**
```
Web Dashboard: http://localhost:3000
API Server: http://localhost:8080
```
### 6. Stop the System
### 6. 停止系统
Press `Ctrl+C` in both terminals
在两个终端中分别按 `Ctrl+C`
---
## 📖 AI Decision Flow
## 📖 AI决策流程
Each decision cycle (default 3 minutes), the system runs the following process:
每个决策周期默认3分钟系统按以下流程运行
```
┌─────────────────────────────────────────────────────┐
│ 1. Analyze Historical Performance (last 20 cycles)
│ 1. 分析历史表现最近20个周期
├─────────────────────────────────────────────────────┤
│ ✓ Calculate overall win rate, avg profit, P/L ratio
│ ✓ Statistics for each coin (win rate, avg P/L)
│ ✓ Identify best/worst coins
│ ✓ List last 5 trade details
│ ✓ 计算整体胜率、平均盈利、盈亏比
│ ✓ 统计各币种表现(胜率、平均盈亏)
│ ✓ 识别最佳/最差币种
│ ✓ 列出最近5笔交易详情
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 2. Get Account Status
│ 2. 获取账户状态
├─────────────────────────────────────────────────────┤
│ • Account equity, available balance
│ • Number of positions, total P/L
│ • Margin usage rate
│ • 账户净值、可用余额
│ • 持仓数量、总盈亏
│ • 保证金使用率
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 3. Analyze Existing Positions (if any)
│ 3. 分析现有持仓(如果有)
├─────────────────────────────────────────────────────┤
│ • Get market data for each position
│ • Calculate technical indicators (RSI, MACD, EMA)
│ • AI decides whether to close positions
│ • 获取每个持仓的市场数据
│ • 计算技术指标(RSIMACDEMA
│ • AI判断是否需要平仓
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 4. Evaluate New Opportunities (candidate coin pool)
│ 4. 评估新机会(候选币种池)
├─────────────────────────────────────────────────────┤
│ • Get AI500 high-score coins (top 20)
│ • Get OI Top growing coins (top 20)
│ • Merge and deduplicate, filter low liquidity
│ • Batch fetch market data and technical indicators
│ • 获取AI500高评分币种前20个
│ • 获取OI Top持仓增长币种前20个
│ • 合并去重,过滤低流动性币种(<15M
│ • 批量获取市场数据和技术指标
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 5. AI Comprehensive Decision
│ 5. AI综合决策
├─────────────────────────────────────────────────────┤
│ • Review historical feedback (win rate, best/worst)
│ • Chain of Thought analysis
│ • Output decision: close/open/hold/wait
│ • Includes leverage, position size, SL, TP
│ • 查看历史反馈(胜率、最佳/最差币种)
│ • Chain of Thought 思维链分析
│ • 输出决策:平仓/开仓/持有/观望
│ • 包含杠杆、仓位、止损、止盈
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 6. Execute Trades
│ 6. 执行交易
├─────────────────────────────────────────────────────┤
│ • Priority: close first, then open
│ • Auto-adapt precision (LOT_SIZE)
│ • Prevent position stacking (reject duplicate)
│ • Auto-cancel all orders after closing
│ • 优先级排序:先平仓,再开仓
│ • 精度自动适配LOT_SIZE
│ • 防止仓位叠加(同币种同方向拒绝开仓)
│ • 平仓后自动取消所有挂单
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 7. Record Logs
│ 7. 记录日志
├─────────────────────────────────────────────────────┤
│ • Save complete decision to decision_logs/ │
│ • Includes CoT, decision JSON, account snapshot
│ • 保存完整决策记录到 decision_logs/
│ • 包含思维链、决策JSON、账户快照、执行结果
└─────────────────────────────────────────────────────┘
```
---
## 🧠 AI Self-Learning Example
## 🧠 AI自我学习示例
### Historical Feedback (Auto-added to Prompt)
### 历史反馈Prompt中自动添加
```markdown
## 📊 Historical Performance Feedback
## 📊 历史表现反馈
### Overall Performance
- **Total Trades**: 15 (Profit: 8 | Loss: 7)
- **Win Rate**: 53.3%
- **Average Profit**: +3.2% | Average Loss: -2.1%
- **Profit/Loss Ratio**: 1.52:1
### 整体表现
- **总交易数**: 15 笔 (盈利: 8 | 亏损: 7)
- **胜率**: 53.3%
- **平均盈利**: +3.2% | 平均亏损: -2.1%
- **盈亏比**: 1.52:1
### Recent Trades
### 最近交易
1. BTCUSDT LONG: 95000.0000 → 97500.0000 = +2.63% ✓
2. ETHUSDT SHORT: 3500.0000 → 3450.0000 = +1.43% ✓
3. SOLUSDT LONG: 185.0000 → 180.0000 = -2.70% ✗
4. BNBUSDT LONG: 610.0000 → 625.0000 = +2.46% ✓
5. ADAUSDT LONG: 0.8500 → 0.8300 = -2.35% ✗
### Coin Performance
- **Best**: BTCUSDT (Win rate 75%, avg +2.5%)
- **Worst**: SOLUSDT (Win rate 25%, avg -1.8%)
### 币种表现
- **最佳**: BTCUSDT (胜率75%, 平均+2.5%)
- **最差**: SOLUSDT (胜率25%, 平均-1.8%)
```
### How AI Uses Feedback
### AI如何使用反馈
1. **Avoid consecutive losers**: Seeing SOLUSDT with 3 consecutive stop-losses, AI avoids or is more cautious
2. **Reinforce successful strategies**: BTC breakout long with 75% win rate, AI continues this pattern
3. **Dynamic style adjustment**: Win rate <40% conservative; P/L ratio >2 → maintain aggressive
4. **Identify market conditions**: Consecutive losses may indicate choppy market, reduce trading frequency
1. **避免连续亏损币种**: 看到SOLUSDT连续3次止损AI会避开或更谨慎
2. **强化成功策略**: BTC突破做多胜率75%AI会继续这个模式
3. **动态调整风格**: 胜率<40%时变保守盈亏比>2时保持激进
4. **识别市场环境**: 连续亏损可能说明市场震荡,减少交易频率
---
## 📊 Web Interface Features
## 📊 Web界面功能
### 1. Competition Page
### 1. 竞赛页面(Competition
- **🏆 Leaderboard**: Real-time ROI ranking, golden border highlights leader
- **📈 Performance Comparison**: Dual AI ROI curve comparison (purple vs blue)
- **⚔️ Head-to-Head**: Direct comparison showing lead margin
- **Real-time Data**: Total equity, P/L%, position count, margin usage
- **🏆 排行榜**: 实时收益率排名,金色边框突出显示领先者
- **📈 性能对比图**: 双AI收益率曲线对比紫色vs蓝色
- **⚔️ Head-to-Head**: 直接对比,显示领先差距
- **实时数据**: 总净值、盈亏%、持仓数、保证金使用率
### 2. Details Page
### 2. 详情页面(Details
- **Equity Curve**: Historical trend chart (USD/percentage toggle)
- **Statistics**: Total cycles, success/fail, open/close stats
- **Position Table**: All position details (entry price, current price, P/L%, liquidation price)
- **AI Decision Logs**: Recent decision records (expandable CoT)
- **账户净值曲线**: 历史走势图(美元/百分比切换)
- **统计信息**: 总周期、成功/失败、开仓/平仓统计
- **持仓表格**: 所有持仓详情(入场价、当前价、盈亏%、强平价)
- **AI决策日志**: 最近决策记录(可展开思维链)
### 3. Real-time Updates
### 3. 实时更新
- System status, account info, position list: **5-second refresh**
- Decision logs, statistics: **10-second refresh**
- Equity charts: **10-second refresh**
- 系统状态、账户信息、持仓列表:**每5秒刷新**
- 决策日志、统计信息:**10秒刷新**
- 收益率图表:**10秒刷新**
---
## 🎛️ API Endpoints
## 🎛️ API接口
### Competition Related
### 竞赛相关
```bash
GET /api/competition # Competition leaderboard (all traders)
GET /api/traders # Trader list
GET /api/competition # 竞赛排行榜(所有trader
GET /api/traders # Trader列表
```
### Single Trader Related
### 单Trader相关
```bash
GET /api/status?trader_id=xxx # System status
GET /api/account?trader_id=xxx # Account info
GET /api/positions?trader_id=xxx # Position list
GET /api/equity-history?trader_id=xxx # Equity history (chart data)
GET /api/decisions/latest?trader_id=xxx # Latest 5 decisions
GET /api/statistics?trader_id=xxx # Statistics
GET /api/status?trader_id=xxx # 系统状态
GET /api/account?trader_id=xxx # 账户信息
GET /api/positions?trader_id=xxx # 持仓列表
GET /api/equity-history?trader_id=xxx # 净值历史(图表数据)
GET /api/decisions/latest?trader_id=xxx # 最新5条决策
GET /api/statistics?trader_id=xxx # 统计信息
```
### System Endpoints
### 系统接口
```bash
GET /health # Health check
GET /api/config # System configuration
GET /health # 健康检查
GET /api/config # 系统配置
```
---
## ⚠️ Important Risk Warnings
## 📝 决策日志格式
### Trading Risks
每次AI决策都会生成详细的JSON日志
1. **Cryptocurrency markets are extremely volatile**, AI decisions don't guarantee profit
2. **Futures trading uses leverage**, losses may exceed principal
3. **Extreme market conditions** may lead to liquidation risk
4. **Funding rates** may affect holding costs
5. **Liquidity risk**: Some coins may experience slippage
### 日志文件路径
```
decision_logs/
├── qwen_trader/
│ └── decision_20251028_153042_cycle15.json
└── deepseek_trader/
└── decision_20251028_153045_cycle15.json
```
### Technical Risks
### 日志内容示例
1. **Network latency** may cause price slippage
2. **API rate limits** may affect trade execution
3. **AI API timeouts** may cause decision failures
4. **System bugs** may trigger unexpected behavior
### Usage Recommendations
**Recommended**
- Use only funds you can afford to lose for testing
- Start with small amounts (recommended 100-500 USDT)
- Regularly check system operation status
- Monitor account balance changes
- Analyze AI decision logs to understand strategy
**Not Recommended**
- Invest all funds or borrowed money
- Run unsupervised for long periods
- Blindly trust AI decisions
- Use without understanding the system
- Run during extreme market volatility
```json
{
"timestamp": "2025-10-28T15:30:42+08:00",
"cycle_number": 15,
"cot_trace": "当前持仓ETHUSDT多头盈利+2.3%,趋势良好继续持有...",
"decision_json": "[{\"symbol\":\"BTCUSDT\",\"action\":\"open_long\"...}]",
"account_state": {
"total_balance": 1045.80,
"available_balance": 823.40,
"position_count": 3,
"margin_used_pct": 21.3
},
"positions": [...],
"candidate_coins": ["BTCUSDT", "ETHUSDT", ...],
"decisions": [
{
"action": "open_long",
"symbol": "BTCUSDT",
"quantity": 0.015,
"leverage": 50,
"price": 95800.0,
"order_id": 123456789,
"success": true
}
],
"execution_log": ["✓ BTCUSDT open_long 成功"],
"success": true
}
```
---
## 🛠️ Common Issues
## 🔧 风险控制详解
### 1. Compilation error: TA-Lib not found
### 单币种仓位限制
**Solution**: Install TA-Lib library
| 币种类型 | 仓位价值上限 | 杠杆 | 保证金占用 | 示例1000U账户 |
|---------|-------------|------|-----------|------------------|
| 山寨币 | 1.5倍净值 | 20x | 7.5% | 最多开1500U仓位 = 75U保证金 |
| BTC/ETH | 10倍净值 | 50x | 20% | 最多开10000U仓位 = 200U保证金 |
### 为什么这样设计?
1. **高杠杆 + 小仓位 = 分散风险**
- 20倍杠杆1500U仓位只需75U保证金
- 可以同时开10+个小仓位,分散单币种风险
2. **单币种风险可控**
- 山寨币仓位≤1.5倍净值5%反向波动 = 7.5%损失
- BTC仓位≤10倍净值2%反向波动 = 20%损失
3. **不限制总保证金使用率**
- AI根据市场机会自主决策保证金使用率
- 上限90%,但不强制满仓
- 有好机会就开仓,没机会就观望
### 防止过度交易
- **同币种同方向不允许重复开仓**: 防止AI连续开同一个仓位导致超限
- **先平仓后开仓**: 换仓时确保先释放保证金
- **止损止盈强制检查**: 风险回报比≥1:2
---
## ⚠️ 重要风险提示
### 交易风险
1. **加密货币市场波动极大**AI决策不保证盈利
2. **合约交易使用杠杆**,亏损可能超过本金
3. **市场极端行情**下可能出现爆仓风险
4. **资金费率**可能影响持仓成本
5. **流动性风险**:某些币种可能出现滑点
### 技术风险
1. **网络延迟**可能导致价格滑点
2. **API限流**可能影响交易执行
3. **AI API超时**可能导致决策失败
4. **系统Bug**可能引发意外行为
### 使用建议
**建议做法**
- 仅使用可承受损失的资金测试
- 从小额资金开始建议100-500 USDT
- 定期检查系统运行状态
- 监控账户余额变化
- 分析AI决策日志理解策略
**不建议做法**
- 投入全部资金或借贷资金
- 长时间无人监控运行
- 盲目信任AI决策
- 在不理解系统的情况下使用
- 在市场极端波动时运行
---
## 🛠️ 常见问题
### 1. 编译错误TA-Lib not found
**解决**: 安装TA-Lib库
```bash
# macOS
brew install ta-lib
@@ -447,103 +522,103 @@ brew install ta-lib
sudo apt-get install libta-lib0-dev
```
### 2. Precision error: Precision is over the maximum
### 2. 精度错误:Precision is over the maximum
**Solution**: System auto-handles precision from Binance LOT_SIZE. If error persists, check network connection.
**解决**: 系统已自动处理精度,从Binance获取LOT_SIZE。如仍报错,检查网络连接。
### 3. AI API timeout
### 3. AI API超时
**Solution**:
- Check if API key is correct
- Check network connection (may need proxy)
- System timeout is set to 120 seconds
**解决**:
- 检查API密钥是否正确
- 检查网络连接(可能需要代理)
- 系统超时时间已设置为120秒
### 4. Frontend can't connect to backend
### 4. 前端无法连接后端
**Solution**:
- Ensure backend is running (http://localhost:8080)
- Check if port 8080 is occupied
- Check browser console for errors
**解决**:
- 确保后端正在运行(http://localhost:8080
- 检查端口8080是否被占用
- 查看浏览器控制台错误信息
### 5. Coin pool API failure
### 5. 币种池API失败
**Solution**:
- Coin pool API is optional
- If API fails, system uses default mainstream coins (BTC, ETH, etc.)
- Check API URL and auth parameter in config.json
**解决**:
- 币种池API是可选的
- 如果API失败系统会使用默认主流币种BTCETH等)
- 检查config.json中的API URL和auth参数
---
## 📈 Performance Optimization Tips
## 📈 性能优化建议
1. **Set reasonable decision cycle**: Recommended 3-5 minutes, avoid over-trading
2. **Control candidate coin count**: System defaults to AI500 top 20 + OI Top top 20
3. **Regularly clean logs**: Avoid excessive disk usage
4. **Monitor API call count**: Avoid triggering Binance rate limits
5. **Test with small capital**: First test with 100-500 USDT for strategy validation
1. **合理设置决策周期**: 建议3-5分钟避免过度交易
2. **控制候选币种数量**: 系统默认分析AI500前20 + OI Top20
3. **定期清理日志**: 避免占用过多磁盘空间
4. **监控API调用次数**: 避免触发Binance限流权重限制
5. **小额资金测试**: 先用100-500 USDT测试策略有效性
---
## 🔄 Changelog
## 🔄 更新日志
### v2.0.0 (2025-10-28)
**Major Updates:**
- ✅ AI self-learning mechanism (historical feedback, performance analysis)
-Multi-trader competition mode (Qwen vs DeepSeek)
- ✅ Binance-style UI (complete Binance interface imitation)
-Performance comparison charts (real-time ROI comparison)
-Risk control optimization (per-coin position limit adjustment)
**重大更新:**
- ✅ AI自我学习机制(历史反馈、表现分析)
-多Trader竞赛模式Qwen vs DeepSeek
- ✅ Binance风格UI完整模仿币安界面
-性能对比图表(收益率实时对比)
-风险控制优化(单币种仓位上限调整)
**Bug Fixes:**
- Fixed hardcoded initial balance issue
- Fixed multi-trader data sync issue
- Optimized chart data alignment (using cycle_number)
**Bug修复:**
- 修复初始余额硬编码问题
- 修复多trader数据同步问题
- 优化图表数据对齐(使用cycle_number
### v1.0.0 (2025-10-27)
- Initial release
- Basic AI trading functionality
- Decision logging system
- Simple Web interface
- 初始版本发布
- 基础AI交易功能
- 决策日志系统
- 简单Web界面
---
## 📄 License
## 📄 开源协议
MIT License - See [LICENSE](LICENSE) file for details
MIT License - 详见 [LICENSE](LICENSE) 文件
---
## 🤝 Contributing
## 🤝 贡献
Issues and Pull Requests are welcome!
欢迎提交IssuePull Request
### Development Guide
### 开发指南
1. Fork the project
2. Create feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to branch (`git push origin feature/AmazingFeature`)
5. Open Pull Request
1. Fork项目
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 开启Pull Request
---
## 📬 Contact
## 📬 联系方式
For questions or suggestions, please submit a [GitHub Issue](https://github.com/tinkle-community/nofx/issues)
如有问题或建议,请提交 [GitHub Issue](https://github.com/yourusername/nofx/issues)
---
## 🙏 Acknowledgments
## 🙏 致谢
- [Binance API](https://binance-docs.github.io/apidocs/futures/en/) - Binance Futures API
- [Binance API](https://binance-docs.github.io/apidocs/futures/cn/) - 币安合约API
- [DeepSeek](https://platform.deepseek.com/) - DeepSeek AI API
- [Qwen](https://dashscope.aliyuncs.com/) - Alibaba Cloud Qwen
- [TA-Lib](https://ta-lib.org/) - Technical indicator library
- [Recharts](https://recharts.org/) - React chart library
- [Qwen](https://dashscope.aliyuncs.com/) - 阿里云通义千问
- [TA-Lib](https://ta-lib.org/) - 技术指标库
- [Recharts](https://recharts.org/) - React图表库
---
**Last Updated**: 2025-10-28
**最后更新**: 2025-10-28
**Explore the possibilities of quantitative trading with the power of AI!**
**用AI的力量探索量化交易的可能性**

View File

@@ -76,6 +76,7 @@ func (s *Server) setupRoutes() {
api.GET("/decisions/latest", s.handleLatestDecisions)
api.GET("/statistics", s.handleStatistics)
api.GET("/equity-history", s.handleEquityHistory)
api.GET("/performance", s.handlePerformance)
}
}
@@ -373,6 +374,32 @@ func (s *Server) handleEquityHistory(c *gin.Context) {
c.JSON(http.StatusOK, history)
}
// handlePerformance AI历史表现分析用于展示AI学习和反思
func (s *Server) handlePerformance(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
// 分析最近20个周期的交易表现
performance, err := trader.GetDecisionLogger().AnalyzePerformance(20)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("分析历史表现失败: %v", err),
})
return
}
c.JSON(http.StatusOK, performance)
}
// Start 启动服务器
func (s *Server) Start() error {
addr := fmt.Sprintf(":%d", s.port)
@@ -387,6 +414,7 @@ func (s *Server) Start() error {
log.Printf(" • GET /api/decisions/latest?trader_id=xxx - 指定trader的最新决策")
log.Printf(" • GET /api/statistics?trader_id=xxx - 指定trader的统计信息")
log.Printf(" • GET /api/equity-history?trader_id=xxx - 指定trader的收益率历史数据")
log.Printf(" • GET /api/performance?trader_id=xxx - 指定trader的AI学习表现分析")
log.Printf(" • GET /health - 健康检查")
log.Println()

84
config.example.go Normal file
View File

@@ -0,0 +1,84 @@
package main
import (
"nofx/trader"
"time"
)
// 配置示例 - 复制到 main.go 中使用
func exampleConfig() trader.AutoTraderConfig {
return trader.AutoTraderConfig{
// ========== API密钥配置 ==========
// 在币安官网申请: https://www.binance.com/zh-CN/my/settings/api-management
BinanceAPIKey: "YOUR_BINANCE_API_KEY", // 必填
BinanceSecretKey: "YOUR_BINANCE_SECRET_KEY", // 必填
// 币种池API可选不填则使用默认池
CoinPoolAPIURL: "", // 留空将从config.json读取
// ========== AI配置 ==========
// 选择一个AI服务商二选一
UseQwen: true, // true=使用阿里云Qwen, false=使用DeepSeek
// DeepSeek配置
// 申请地址: https://platform.deepseek.com/
DeepSeekKey: "sk-your-deepseek-api-key",
// 阿里云Qwen配置
// 申请地址: https://dashscope.aliyun.com/
QwenKey: "sk-your-qwen-api-key",
// ========== 交易周期 ==========
// AI决策频率建议3-5分钟
ScanInterval: 3 * time.Minute,
// ========== 风险控制 ==========
// 注意这些仅作为提示AI可以自主决定实际参数
MaxDailyLoss: 5.0, // 最大日亏损5%(触发后暂停)
MaxDrawdown: 10.0, // 最大回撤10%(触发后暂停)
StopTradingTime: 30 * time.Minute, // 触发风控后暂停30分钟
}
}
// ========== AI决策原则内置在系统中 ==========
//
// 以下参数由AI根据市场情况自主决定无需配置
//
// 1. 杠杆倍数: 1-20倍
// - AI会根据波动率和信心度选择
// - 高波动 → 低杠杆
// - 低波动 → 高杠杆
//
// 2. 仓位大小: USD金额
// - AI会根据账户净值和风险评估决定
// - 建议单笔风险2-5%
//
// 3. 止损止盈价格:
// - AI会根据技术指标动态设置
// - 风险回报比建议 ≥ 1:2
//
// 4. 开仓时机:
// - 趋势明确、技术指标一致
// - RSI、MACD、EMA多重确认
//
// 5. 平仓时机:
// - 到达止损/止盈
// - 趋势反转信号
// - 大额亏损保护
//
// ========== 快速开始 ==========
//
// 1. 配置API密钥main.go中
// 2. 选择AI服务商DeepSeek或Qwen
// 3. 编译: go build -o nofx-auto
// 4. 运行: ./nofx-auto
// 5. 观察AI的思维链分析和决策
//
// ========== 风险提示 ==========
//
// ⚠️ 建议先用小额资金100-1000 USDT测试
// ⚠️ 密切监控系统运行,特别是初期
// ⚠️ 设置币安API的IP白名单
// ⚠️ 定期检查持仓和盈亏
// ⚠️ 加密货币交易有风险,投资需谨慎

View File

@@ -21,8 +21,8 @@
"scan_interval_minutes": 3
}
],
"coin_pool_api_url": "http://x.x.x.x:x/api/ai500/list?auth=YOUR_AUTH_TOKEN",
"oi_top_api_url": "http://x.x.x.x:x/api/oi/top?auth=YOUR_AUTH_TOKEN",
"coin_pool_api_url": "http://x.x.x.x:x/api/ai500/list?auth=",
"oi_top_api_url": "http://x.x.x.x:x/api/oi/top?auth=",
"api_server_port": 8080,
"max_daily_loss": 5.0,
"max_drawdown": 10.0,

View File

@@ -267,42 +267,42 @@ type Statistics struct {
// TradeOutcome 单笔交易结果
type TradeOutcome struct {
Symbol string // 币种
Side string // long/short
OpenPrice float64 // 开仓价
ClosePrice float64 // 平仓价
PnL float64 // 盈亏USDT
PnLPct float64 // 盈亏百分比
Duration string // 持仓时长
OpenTime time.Time // 开仓时间
CloseTime time.Time // 平仓时间
WasStopLoss bool // 是否止损
Symbol string `json:"symbol"` // 币种
Side string `json:"side"` // long/short
OpenPrice float64 `json:"open_price"` // 开仓价
ClosePrice float64 `json:"close_price"` // 平仓价
PnL float64 `json:"pn_l"` // 盈亏USDT
PnLPct float64 `json:"pn_l_pct"` // 盈亏百分比
Duration string `json:"duration"` // 持仓时长
OpenTime time.Time `json:"open_time"` // 开仓时间
CloseTime time.Time `json:"close_time"` // 平仓时间
WasStopLoss bool `json:"was_stop_loss"` // 是否止损
}
// PerformanceAnalysis 交易表现分析
type PerformanceAnalysis struct {
TotalTrades int // 总交易数
WinningTrades int // 盈利交易数
LosingTrades int // 亏损交易数
WinRate float64 // 胜率
AvgWin float64 // 平均盈利
AvgLoss float64 // 平均亏损
ProfitFactor float64 // 盈亏比
RecentTrades []TradeOutcome // 最近N笔交易
SymbolStats map[string]*SymbolPerformance // 各币种表现
BestSymbol string // 表现最好的币种
WorstSymbol string // 表现最差的币种
TotalTrades int `json:"total_trades"` // 总交易数
WinningTrades int `json:"winning_trades"` // 盈利交易数
LosingTrades int `json:"losing_trades"` // 亏损交易数
WinRate float64 `json:"win_rate"` // 胜率
AvgWin float64 `json:"avg_win"` // 平均盈利
AvgLoss float64 `json:"avg_loss"` // 平均亏损
ProfitFactor float64 `json:"profit_factor"` // 盈亏比
RecentTrades []TradeOutcome `json:"recent_trades"` // 最近N笔交易
SymbolStats map[string]*SymbolPerformance `json:"symbol_stats"` // 各币种表现
BestSymbol string `json:"best_symbol"` // 表现最好的币种
WorstSymbol string `json:"worst_symbol"` // 表现最差的币种
}
// SymbolPerformance 币种表现统计
type SymbolPerformance struct {
Symbol string // 币种
TotalTrades int // 交易次数
WinningTrades int // 盈利次数
LosingTrades int // 亏损次数
WinRate float64 // 胜率
TotalPnL float64 // 总盈亏
AvgPnL float64 // 平均盈亏
Symbol string `json:"symbol"` // 币种
TotalTrades int `json:"total_trades"` // 交易次数
WinningTrades int `json:"winning_trades"` // 盈利次数
LosingTrades int `json:"losing_trades"` // 亏损次数
WinRate float64 `json:"win_rate"` // 胜率
TotalPnL float64 `json:"total_pn_l"` // 总盈亏
AvgPnL float64 `json:"avg_pn_l"` // 平均盈亏
}
// AnalyzePerformance 分析最近N个周期的交易表现
@@ -314,12 +314,14 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna
if len(records) == 0 {
return &PerformanceAnalysis{
SymbolStats: make(map[string]*SymbolPerformance),
RecentTrades: []TradeOutcome{},
SymbolStats: make(map[string]*SymbolPerformance),
}, nil
}
analysis := &PerformanceAnalysis{
SymbolStats: make(map[string]*SymbolPerformance),
RecentTrades: []TradeOutcome{},
SymbolStats: make(map[string]*SymbolPerformance),
}
// 追踪持仓状态symbol -> {side, openPrice, openTime}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"nofx/pool"
"regexp"
"strings"
"time"
)
@@ -304,9 +305,10 @@ func buildFullDecisionPrompt(ctx *TradingContext) string {
// AI决策要求
sb.WriteString("## 🎯 任务\n\n")
sb.WriteString("分析市场数据,自主决策:\n")
sb.WriteString("1. 评估现有持仓 → 持有或平仓\n")
sb.WriteString(fmt.Sprintf("2. 从%d个候选币种中找交易机会\n", len(ctx.MarketDataMap)))
sb.WriteString("3. 开新仓(如果有机会\n\n")
sb.WriteString("1. **如有历史数据,先进行自我反思**:回顾之前的交易,总结经验教训\n")
sb.WriteString("2. 评估现有持仓 → 持有或平仓\n")
sb.WriteString(fmt.Sprintf("3. 从%d个候选币种中找交易机会\n", len(ctx.MarketDataMap)))
sb.WriteString("4. 开新仓(如果有机会)\n\n")
sb.WriteString("## 📋 规则\n\n")
sb.WriteString(fmt.Sprintf("1. **单币种仓位上限**: 山寨币≤%.0f USDT | BTC/ETH≤%.0f USDT\n", ctx.Account.TotalEquity*1.5, ctx.Account.TotalEquity*10))
@@ -315,36 +317,41 @@ func buildFullDecisionPrompt(ctx *TradingContext) string {
sb.WriteString("4. **风险回报比**: ≥1:2\n\n")
sb.WriteString("### 📤 输出格式\n\n")
sb.WriteString("**思维链分析** (纯文本)\n")
sb.WriteString("- 分析持仓 → 找新机会 → 账户检查\n")
sb.WriteString("- **最后必须列出最终决策摘要**例如持有XX平仓XX开多XX开空XX\n\n")
sb.WriteString("**重要严格按照JSON格式输出所有字符串值必须用双引号包裹**\n\n")
sb.WriteString("先输出思维链分析(纯文本)然后输出JSON数组\n\n")
sb.WriteString("**思维链分析**:\n")
sb.WriteString("1. **历史经验反思**(如有历史数据): 回顾表现,总结教训\n")
sb.WriteString("2. **市场分析**: 分析BTC趋势和当前持仓\n")
sb.WriteString("3. **机会识别**: 从候选币种中找交易机会\n")
sb.WriteString("4. **风险控制**: 检查账户保证金和仓位限制\n")
sb.WriteString("5. **最终决策摘要**: 列出所有决策\n\n")
sb.WriteString("---\n\n")
sb.WriteString("**决策JSON** (不要```标记)\n")
sb.WriteString("**JSON决策数组** (不要```标记,所有字符串必须用双引号):\n")
sb.WriteString("[\n")
sb.WriteString(" {\"symbol\": \"BTCUSDT\", \"action\": \"open_long\", \"leverage\": 50, \"position_size_usd\": 15000, \"stop_loss\": 92000, \"take_profit\": 98000, \"reasoning\": \"突破做多\"},\n")
sb.WriteString(" {\"symbol\": \"ETHUSDT\", \"action\": \"hold\", \"reasoning\": \"持续观察\"}\n")
sb.WriteString("]\n\n")
sb.WriteString("**action类型**: open_long | open_short | close_long | close_short | hold | wait\n")
sb.WriteString("**开仓必填**: leverage, position_size_usd, stop_loss, take_profit\n")
sb.WriteString("**position_size_usd**: 仓位价值(非保证金),保证金=position_size_usd/leverage\n\n")
sb.WriteString("**注意**: reasoning字段必须用双引号包裹例如\"reasoning\": \"这里是理由\"\n\n")
sb.WriteString("### 📝 完整示例\n\n")
// 简化示例仓位(使用新的仓位上限)
btcSize := ctx.Account.TotalEquity * 8 // BTC示例8倍净值不超过10倍上限
altSize := ctx.Account.TotalEquity * 1 // 山寨币示例1倍净值不超过1.5倍上限)
sb.WriteString("**思维链**:\n")
sb.WriteString("当前持仓ETHUSDT多头盈利+2.3%,趋势良好继续持有。\n")
sb.WriteString("新机会BTC突破上涨MACD金叉资金费率低做多信号强。\n")
sb.WriteString(" SOLUSDT回调至支撑位出现反弹信号可小仓位做多。\n")
sb.WriteString("账户可用余额充足保证金使用率32%,可分散开仓。\n")
sb.WriteString("**最终决策**持有ETHUSDT开多BTCUSDT(8倍净值)开多SOLUSDT(1倍净值)。\n\n")
sb.WriteString("【历史经验反思】\n")
sb.WriteString("回顾最近10笔交易胜率40%盈亏比0.8:1表现欠佳。\n")
sb.WriteString("SOLUSDT做多3次全部止损该币种波动大暂时避开。\n")
sb.WriteString("BTCUSDT做多2次1胜1负可继续关注。\n\n")
sb.WriteString("【市场分析】\n")
sb.WriteString("BTC突破上涨MACD金叉趋势向上。\n")
sb.WriteString("当前持仓ETHUSDT多头盈利+2.3%,继续持有。\n\n")
sb.WriteString("【最终决策】持有ETHUSDT开多BTCUSDT。\n\n")
sb.WriteString("---\n\n")
sb.WriteString("[\n")
sb.WriteString(" {\"symbol\": \"ETHUSDT\", \"action\": \"hold\", \"reasoning\": \"盈利良好,趋势延续\"},\n")
sb.WriteString(fmt.Sprintf(" {\"symbol\": \"BTCUSDT\", \"action\": \"open_long\", \"leverage\": 50, \"position_size_usd\": %.0f, \"stop_loss\": 92000, \"take_profit\": 98000, \"reasoning\": \"突破做多\"},\n", btcSize))
sb.WriteString(fmt.Sprintf(" {\"symbol\": \"SOLUSDT\", \"action\": \"open_long\", \"leverage\": 20, \"position_size_usd\": %.0f, \"stop_loss\": 180, \"take_profit\": 210, \"reasoning\": \"支撑位反弹\"}\n", altSize))
sb.WriteString(fmt.Sprintf(" {\"symbol\": \"BTCUSDT\", \"action\": \"open_long\", \"leverage\": 50, \"position_size_usd\": %.0f, \"stop_loss\": 92000, \"take_profit\": 98000, \"reasoning\": \"突破确认,历史表现佳\"}\n", btcSize))
sb.WriteString("]\n\n")
sb.WriteString("现在请开始分析并给出你的决策!\n")
@@ -481,22 +488,20 @@ func formatMarketDataBrief(data *MarketData) string {
// parseFullDecisionResponse 解析AI的完整决策响应
func parseFullDecisionResponse(aiResponse string, accountEquity float64) (*AIFullDecision, error) {
// 1. 提取 cot_trace思维链
// 1. 提取思维链
cotTrace := extractCoTTrace(aiResponse)
// 2. 提取 JSON 决策列表
// 2. 提取JSON决策列表
decisions, err := extractDecisions(aiResponse)
if err != nil {
// 即使JSON解析失败也返回思维链
return &AIFullDecision{
CoTTrace: cotTrace,
Decisions: []TradingDecision{},
}, fmt.Errorf("提取决策失败: %w\n\n=== AI思维链分析 ===\n%s", err, cotTrace)
}
// 3. 验证决策(包含仓位价值上限检查)
// 3. 验证决策
if err := validateDecisions(decisions, accountEquity); err != nil {
// 验证失败时,也返回思维链和决策,但标记为错误
return &AIFullDecision{
CoTTrace: cotTrace,
Decisions: decisions,
@@ -539,6 +544,12 @@ func extractDecisions(response string) ([]TradingDecision, error) {
jsonContent := strings.TrimSpace(response[arrayStart : arrayEnd+1])
// 🔧 修复常见的JSON格式错误缺少引号的字段值
// 匹配: "reasoning": 内容"} 或 "reasoning": 内容} (没有引号)
// 修复为: "reasoning": "内容"}
// 使用简单的字符串扫描而不是正则表达式
jsonContent = fixMissingQuotes(jsonContent)
// 解析JSON
var decisions []TradingDecision
if err := json.Unmarshal([]byte(jsonContent), &decisions); err != nil {
@@ -548,6 +559,15 @@ func extractDecisions(response string) ([]TradingDecision, error) {
return decisions, nil
}
// fixMissingQuotes 修复缺少引号的reasoning字段
func fixMissingQuotes(jsonStr string) string {
// 匹配: "reasoning": 内容"} 或 "reasoning": 内容}
// 不匹配: "reasoning": "已经有引号"
// 替换为: "reasoning": "内容"}
re := regexp.MustCompile(`"reasoning":\s*([^"}\n,][^}\n,]*?)([}\n,])`)
return re.ReplaceAllString(jsonStr, `"reasoning": "$1"$2`)
}
// validateDecisions 验证所有决策(需要账户信息)
func validateDecisions(decisions []TradingDecision, accountEquity float64) error {
for i, decision := range decisions {

View File

@@ -262,10 +262,13 @@ func callDeepSeekAPIOnce(prompt string) (string, error) {
"content": prompt,
},
},
"temperature": 0.7,
"temperature": 0.5, // 降低temperature以提高JSON格式稳定性
"max_tokens": 2000,
}
// 注意response_format 参数仅 OpenAI 支持DeepSeek/Qwen 不支持
// 我们通过强化 prompt 和后处理来确保 JSON 格式正确
jsonData, err := json.Marshal(requestBody)
if err != nil {
return "", fmt.Errorf("序列化请求失败: %w", err)

View File

@@ -19,7 +19,7 @@ type CoinPoolConfig struct {
}
var coinPoolConfig = CoinPoolConfig{
APIURL: "", // 将通过config.json配置或SetCoinPoolAPI()设置
APIURL: "",
Timeout: 30 * time.Second, // 增加到30秒
CacheDir: "coin_pool_cache",
}
@@ -359,7 +359,7 @@ var oiTopConfig = struct {
Timeout time.Duration
CacheDir string
}{
APIURL: "", // 将通过config.json配置或SetOITopAPI()设置
APIURL: "",
Timeout: 30 * time.Second,
CacheDir: "coin_pool_cache",
}

View File

@@ -3,6 +3,7 @@ import useSWR from 'swr';
import { api } from './lib/api';
import { EquityChart } from './components/EquityChart';
import { CompetitionPage } from './components/CompetitionPage';
import AILearning from './components/AILearning';
import type {
SystemStatus,
AccountInfo,
@@ -97,7 +98,7 @@ function App() {
<div className="min-h-screen" style={{ background: '#0B0E11', color: '#EAECEF' }}>
{/* Header - Binance Style */}
<header className="glass sticky top-0 z-50 backdrop-blur-xl">
<div className="max-w-7xl mx-auto px-4 py-4">
<div className="max-w-[1920px] mx-auto px-6 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full flex items-center justify-center text-xl" style={{ background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)' }}>
@@ -179,7 +180,7 @@ function App() {
</header>
{/* Main Content */}
<main className="max-w-7xl mx-auto px-4 py-6">
<main className="max-w-[1920px] mx-auto px-6 py-6">
{currentPage === 'competition' ? (
<CompetitionPage />
) : (
@@ -197,7 +198,7 @@ function App() {
{/* Footer */}
<footer className="mt-16" style={{ borderTop: '1px solid #2B3139', background: '#181A20' }}>
<div className="max-w-7xl mx-auto px-4 py-6 text-center text-sm" style={{ color: '#5E6673' }}>
<div className="max-w-[1920px] mx-auto px-6 py-6 text-center text-sm" style={{ color: '#5E6673' }}>
<p>NOFX - AI Trading Competition System</p>
<p className="mt-1"> Trading involves risk. Use at your own discretion.</p>
</div>
@@ -312,46 +313,17 @@ function TraderDetailsPage({
/>
</div>
{/* Equity Chart */}
<div className="mb-8 animate-slide-in" style={{ animationDelay: '0.1s' }}>
<EquityChart traderId={selectedTrader.trader_id} />
</div>
{/* Statistics */}
{stats && (
<div className="binance-card p-6 mb-6 animate-slide-in" style={{ animationDelay: '0.2s' }}>
<h2 className="text-xl font-bold mb-5 flex items-center gap-2" style={{ color: '#EAECEF' }}>
📊 Statistics
</h2>
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<div>
<div className="text-xs" style={{ color: '#848E9C' }}>Total Cycles</div>
<div className="text-2xl font-bold" style={{ color: '#EAECEF' }}>{stats.total_cycles}</div>
</div>
<div>
<div className="text-xs" style={{ color: '#848E9C' }}>Successful</div>
<div className="text-2xl font-bold" style={{ color: '#0ECB81' }}>
{stats.successful_cycles}
</div>
</div>
<div>
<div className="text-xs" style={{ color: '#848E9C' }}>Failed</div>
<div className="text-2xl font-bold" style={{ color: '#F6465D' }}>{stats.failed_cycles}</div>
</div>
<div>
<div className="text-xs" style={{ color: '#848E9C' }}>Open Positions</div>
<div className="text-2xl font-bold" style={{ color: '#EAECEF' }}>{stats.total_open_positions}</div>
</div>
<div>
<div className="text-xs" style={{ color: '#848E9C' }}>Close Positions</div>
<div className="text-2xl font-bold" style={{ color: '#EAECEF' }}>{stats.total_close_positions}</div>
</div>
{/* 主要内容区:左右分屏 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
{/* 左侧:图表 + 持仓 */}
<div className="space-y-6">
{/* Equity Chart */}
<div className="animate-slide-in" style={{ animationDelay: '0.1s' }}>
<EquityChart traderId={selectedTrader.trader_id} />
</div>
</div>
)}
{/* Positions */}
<div className="binance-card p-6 mb-6 animate-slide-in" style={{ animationDelay: '0.3s' }}>
{/* Current Positions */}
<div className="binance-card p-6 animate-slide-in" style={{ animationDelay: '0.15s' }}>
<div className="flex items-center justify-between mb-5">
<h2 className="text-xl font-bold flex items-center gap-2" style={{ color: '#EAECEF' }}>
📈 Current Positions
@@ -423,33 +395,51 @@ function TraderDetailsPage({
<div className="text-sm"></div>
</div>
)}
</div>
</div>
{/* 左侧结束 */}
{/* 右侧Recent Decisions - 卡片容器 */}
<div className="binance-card p-6 animate-slide-in h-fit lg:sticky lg:top-24 lg:max-h-[calc(100vh-120px)]" style={{ animationDelay: '0.2s' }}>
{/* 标题 */}
<div className="flex items-center gap-3 mb-5 pb-4 border-b" style={{ borderColor: '#2B3139' }}>
<div className="w-10 h-10 rounded-xl flex items-center justify-center text-xl" style={{
background: 'linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%)',
boxShadow: '0 4px 14px rgba(99, 102, 241, 0.4)'
}}>
🧠
</div>
<div>
<h2 className="text-xl font-bold" style={{ color: '#EAECEF' }}>Recent Decisions</h2>
{decisions && decisions.length > 0 && (
<div className="text-xs" style={{ color: '#848E9C' }}>
Last {decisions.length} trading cycles
</div>
)}
</div>
</div>
{/* 决策列表 - 可滚动 */}
<div className="space-y-4 overflow-y-auto pr-2" style={{ maxHeight: 'calc(100vh - 280px)' }}>
{decisions && decisions.length > 0 ? (
decisions.map((decision, i) => (
<DecisionCard key={i} decision={decision} />
))
) : (
<div className="py-16 text-center">
<div className="text-6xl mb-4 opacity-30">🧠</div>
<div className="text-lg font-semibold mb-2" style={{ color: '#EAECEF' }}>No Decisions Yet</div>
<div className="text-sm" style={{ color: '#848E9C' }}>AI trading decisions will appear here</div>
</div>
)}
</div>
</div>
{/* 右侧结束 */}
</div>
{/* Recent Decisions */}
<div className="binance-card p-6 animate-slide-in" style={{ animationDelay: '0.4s' }}>
<div className="flex items-center justify-between mb-5">
<h2 className="text-xl font-bold flex items-center gap-2" style={{ color: '#EAECEF' }}>
🧠 Recent Decisions
</h2>
{decisions && decisions.length > 0 && (
<div className="text-xs px-3 py-1 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', color: '#F0B90B', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
Last {decisions.length} Cycles
</div>
)}
</div>
{decisions && decisions.length > 0 ? (
<div className="space-y-4">
{decisions.map((decision, i) => (
<DecisionCard key={i} decision={decision} />
))}
</div>
) : (
<div className="text-center py-16" style={{ color: '#848E9C' }}>
<div className="text-6xl mb-4 opacity-50">🧠</div>
<div className="text-lg font-semibold mb-2"></div>
<div className="text-sm">AI交易决策将在这里显示</div>
</div>
)}
{/* AI Learning & Performance Analysis */}
<div className="mb-6 animate-slide-in" style={{ animationDelay: '0.3s' }}>
<AILearning traderId={selectedTrader.trader_id} />
</div>
</div>
);

View File

@@ -0,0 +1,688 @@
import { useEffect, useState } from 'react';
import useSWR from 'swr';
interface TradeOutcome {
symbol: string;
side: string;
open_price: number;
close_price: number;
pn_l: number;
pn_l_pct: number;
duration: string;
open_time: string;
close_time: string;
was_stop_loss: boolean;
}
interface SymbolPerformance {
symbol: string;
total_trades: number;
winning_trades: number;
losing_trades: number;
win_rate: number;
total_pn_l: number;
avg_pn_l: number;
}
interface PerformanceAnalysis {
total_trades: number;
winning_trades: number;
losing_trades: number;
win_rate: number;
avg_win: number;
avg_loss: number;
profit_factor: number;
recent_trades: TradeOutcome[];
symbol_stats: { [key: string]: SymbolPerformance };
best_symbol: string;
worst_symbol: string;
}
interface AILearningProps {
traderId: string;
}
interface DecisionRecord {
timestamp: string;
cycle_number: number;
cot_trace: string;
success: boolean;
}
const fetcher = (url: string) => fetch(url).then(res => res.json());
export default function AILearning({ traderId }: AILearningProps) {
const { data: performance, error } = useSWR<PerformanceAnalysis>(
`http://localhost:8080/api/performance?trader_id=${traderId}`,
fetcher,
{ refreshInterval: 10000 }
);
// 获取最新的决策记录查看AI的思考过程
const { data: latestDecisions } = useSWR<DecisionRecord[]>(
`http://localhost:8080/api/decisions/latest?trader_id=${traderId}`,
fetcher,
{ refreshInterval: 10000 }
);
if (error) {
return (
<div className="rounded p-6" style={{ background: '#1E2329', border: '1px solid #2B3139' }}>
<div style={{ color: '#F6465D' }}> AI学习数据失败</div>
</div>
);
}
if (!performance) {
return (
<div className="rounded p-6" style={{ background: '#1E2329', border: '1px solid #2B3139' }}>
<div style={{ color: '#848E9C' }}>📊 ...</div>
</div>
);
}
if (!performance || performance.total_trades === 0) {
return (
<div className="rounded p-6" style={{ background: '#1E2329', border: '1px solid #2B3139' }}>
<div className="flex items-center gap-2 mb-2">
<span className="text-xl">🧠</span>
<h2 className="text-lg font-bold" style={{ color: '#EAECEF' }}>AI </h2>
</div>
<div style={{ color: '#848E9C' }}>
</div>
</div>
);
}
// 安全地获取symbol_stats
const symbolStats = performance.symbol_stats || {};
const symbolStatsList = Object.values(symbolStats).filter(stat => stat != null).sort(
(a, b) => (b.total_pn_l || 0) - (a.total_pn_l || 0)
);
// 提取AI的最新反思从CoT trace中
const latestReflection = extractReflectionFromCoT(latestDecisions?.[0]?.cot_trace);
return (
<div className="space-y-6">
{/* 标题区 - 更简洁 */}
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-xl flex items-center justify-center text-2xl" style={{
background: 'linear-gradient(135deg, #8B5CF6 0%, #6366F1 100%)',
boxShadow: '0 4px 14px rgba(139, 92, 246, 0.4)'
}}>
🧠
</div>
<div>
<h2 className="text-2xl font-bold" style={{ color: '#EAECEF' }}>AI Learning & Reflection</h2>
<p className="text-sm" style={{ color: '#848E9C' }}>
{performance.total_trades} trades analyzed · Real-time evolution
</p>
</div>
</div>
{/* 主要内容:现代化网格布局 */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
{/* 左侧大区域AI反思 + 核心指标 (5列) */}
<div className="lg:col-span-5 space-y-6">
{/* AI最新反思 - 渐变卡片 */}
{latestReflection && latestDecisions && latestDecisions.length > 0 && (
<div className="rounded-2xl p-6 relative overflow-hidden" style={{
background: 'linear-gradient(135deg, #1E1B4B 0%, #312E81 50%, #1E293B 100%)',
border: '1px solid rgba(139, 92, 246, 0.2)',
boxShadow: '0 10px 40px rgba(139, 92, 246, 0.15)'
}}>
{/* 背景装饰 */}
<div className="absolute top-0 right-0 w-64 h-64 rounded-full opacity-10" style={{
background: 'radial-gradient(circle, #8B5CF6 0%, transparent 70%)',
filter: 'blur(40px)'
}} />
<div className="relative">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg flex items-center justify-center text-xl" style={{
background: 'rgba(139, 92, 246, 0.2)',
border: '1px solid rgba(139, 92, 246, 0.3)'
}}>
🎯
</div>
<div>
<h3 className="text-base font-bold" style={{ color: '#C4B5FD' }}>
Latest Reflection
</h3>
<p className="text-xs" style={{ color: '#94A3B8' }}>
Cycle #{latestDecisions[0].cycle_number} · {new Date(latestDecisions[0].timestamp).toLocaleTimeString()}
</p>
</div>
</div>
<div className="rounded-xl p-4 text-sm leading-relaxed whitespace-pre-wrap" style={{
background: 'rgba(0, 0, 0, 0.4)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(139, 92, 246, 0.1)',
color: '#DDD6FE',
fontFamily: 'ui-sans-serif, system-ui'
}}>
{latestReflection}
</div>
{latestDecisions[0].cot_trace && (
<details className="mt-4">
<summary className="cursor-pointer text-sm font-semibold flex items-center gap-2 hover:opacity-80 transition-opacity" style={{ color: '#A78BFA' }}>
<span>📋 Full Chain of Thought</span>
</summary>
<div className="mt-3 rounded-xl p-4 text-xs leading-relaxed whitespace-pre-wrap max-h-80 overflow-y-auto" style={{
background: 'rgba(0, 0, 0, 0.5)',
border: '1px solid rgba(139, 92, 246, 0.15)',
color: '#A5B4FC',
fontFamily: 'ui-monospace, monospace'
}}>
{latestDecisions[0].cot_trace}
</div>
</details>
)}
</div>
</div>
)}
{/* 核心指标网格 - 玻璃态设计 */}
<div className="grid grid-cols-2 gap-4">
{/* 总交易数 */}
<div className="rounded-xl p-4 backdrop-blur-sm" style={{
background: 'rgba(30, 35, 41, 0.6)',
border: '1px solid rgba(99, 102, 241, 0.2)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
}}>
<div className="text-xs font-semibold mb-2" style={{ color: '#94A3B8' }}>Total Trades</div>
<div className="text-3xl font-bold mono" style={{ color: '#E0E7FF' }}>
{performance.total_trades}
</div>
</div>
{/* 胜率 */}
<div className="rounded-xl p-4 backdrop-blur-sm" style={{
background: (performance.win_rate || 0) >= 50
? 'rgba(14, 203, 129, 0.1)'
: 'rgba(246, 70, 93, 0.1)',
border: `1px solid ${(performance.win_rate || 0) >= 50 ? 'rgba(14, 203, 129, 0.3)' : 'rgba(246, 70, 93, 0.3)'}`,
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
}}>
<div className="text-xs font-semibold mb-2" style={{ color: '#94A3B8' }}>Win Rate</div>
<div className="text-3xl font-bold mono" style={{
color: (performance.win_rate || 0) >= 50 ? '#10B981' : '#F87171'
}}>
{(performance.win_rate || 0).toFixed(1)}%
</div>
<div className="text-xs mt-1" style={{ color: '#94A3B8' }}>
{performance.winning_trades || 0}W / {performance.losing_trades || 0}L
</div>
</div>
{/* 平均盈利 */}
<div className="rounded-xl p-4 backdrop-blur-sm" style={{
background: 'rgba(14, 203, 129, 0.08)',
border: '1px solid rgba(14, 203, 129, 0.2)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
}}>
<div className="text-xs font-semibold mb-2" style={{ color: '#94A3B8' }}>Avg Win</div>
<div className="text-3xl font-bold mono" style={{ color: '#10B981' }}>
+{(performance.avg_win || 0).toFixed(2)}%
</div>
</div>
{/* 平均亏损 */}
<div className="rounded-xl p-4 backdrop-blur-sm" style={{
background: 'rgba(246, 70, 93, 0.08)',
border: '1px solid rgba(246, 70, 93, 0.2)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)'
}}>
<div className="text-xs font-semibold mb-2" style={{ color: '#94A3B8' }}>Avg Loss</div>
<div className="text-3xl font-bold mono" style={{ color: '#F87171' }}>
{(performance.avg_loss || 0).toFixed(2)}%
</div>
</div>
</div>
{/* 盈亏比 - 突出显示 */}
<div className="rounded-2xl p-6 relative overflow-hidden" style={{
background: 'linear-gradient(135deg, rgba(240, 185, 11, 0.15) 0%, rgba(252, 213, 53, 0.05) 100%)',
border: '1px solid rgba(240, 185, 11, 0.3)',
boxShadow: '0 8px 24px rgba(240, 185, 11, 0.2)'
}}>
<div className="absolute top-0 right-0 w-40 h-40 rounded-full opacity-20" style={{
background: 'radial-gradient(circle, #F0B90B 0%, transparent 70%)',
filter: 'blur(30px)'
}} />
<div className="relative flex items-center justify-between">
<div>
<div className="text-sm font-semibold mb-1" style={{ color: '#FCD34D' }}>Profit Factor</div>
<div className="text-xs" style={{ color: '#94A3B8' }}>
Avg Win ÷ Avg Loss
</div>
</div>
<div className="text-5xl font-bold mono" style={{
color: (performance.profit_factor || 0) >= 2.0 ? '#10B981' :
(performance.profit_factor || 0) >= 1.5 ? '#F0B90B' :
(performance.profit_factor || 0) >= 1.0 ? '#FB923C' : '#F87171'
}}>
{(performance.profit_factor || 0) > 0 ? (performance.profit_factor || 0).toFixed(2) : 'N/A'}
</div>
</div>
<div className="mt-3 text-xs font-semibold" style={{
color: (performance.profit_factor || 0) >= 2.0 ? '#10B981' :
(performance.profit_factor || 0) >= 1.5 ? '#F0B90B' : '#94A3B8'
}}>
{(performance.profit_factor || 0) >= 2.0 && '🔥 Excellent - Strong profitability'}
{(performance.profit_factor || 0) >= 1.5 && (performance.profit_factor || 0) < 2.0 && '✓ Good - Stable profits'}
{(performance.profit_factor || 0) >= 1.0 && (performance.profit_factor || 0) < 1.5 && '⚠️ Fair - Needs optimization'}
{(performance.profit_factor || 0) > 0 && (performance.profit_factor || 0) < 1.0 && '❌ Poor - Losses exceed gains'}
</div>
</div>
</div>
{/* 左侧结束 */}
{/* 中间列:数据表格 (4列) */}
<div className="lg:col-span-4 space-y-6">
{/* 最佳/最差币种 */}
{(performance.best_symbol || performance.worst_symbol) && (
<div className="grid grid-cols-2 gap-4">
{performance.best_symbol && (
<div className="rounded-xl p-5 backdrop-blur-sm" style={{
background: 'linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(14, 203, 129, 0.05) 100%)',
border: '1px solid rgba(16, 185, 129, 0.3)',
boxShadow: '0 4px 16px rgba(16, 185, 129, 0.1)'
}}>
<div className="flex items-center gap-2 mb-3">
<span className="text-xl">🏆</span>
<span className="text-xs font-semibold" style={{ color: '#6EE7B7' }}>Best Performer</span>
</div>
<div className="text-2xl font-bold mono mb-1" style={{ color: '#10B981' }}>
{performance.best_symbol}
</div>
{symbolStats[performance.best_symbol] && (
<div className="text-sm font-semibold" style={{ color: '#6EE7B7' }}>
{symbolStats[performance.best_symbol].total_pn_l > 0 ? '+' : ''}
{symbolStats[performance.best_symbol].total_pn_l.toFixed(2)}% P&L
</div>
)}
</div>
)}
{performance.worst_symbol && (
<div className="rounded-xl p-5 backdrop-blur-sm" style={{
background: 'linear-gradient(135deg, rgba(248, 113, 113, 0.15) 0%, rgba(246, 70, 93, 0.05) 100%)',
border: '1px solid rgba(248, 113, 113, 0.3)',
boxShadow: '0 4px 16px rgba(248, 113, 113, 0.1)'
}}>
<div className="flex items-center gap-2 mb-3">
<span className="text-xl">📉</span>
<span className="text-xs font-semibold" style={{ color: '#FCA5A5' }}>Worst Performer</span>
</div>
<div className="text-2xl font-bold mono mb-1" style={{ color: '#F87171' }}>
{performance.worst_symbol}
</div>
{symbolStats[performance.worst_symbol] && (
<div className="text-sm font-semibold" style={{ color: '#FCA5A5' }}>
{symbolStats[performance.worst_symbol].total_pn_l > 0 ? '+' : ''}
{symbolStats[performance.worst_symbol].total_pn_l.toFixed(2)}% P&L
</div>
)}
</div>
)}
</div>
)}
{/* 币种表现统计 - 现代化表格 */}
{symbolStatsList.length > 0 && (
<div className="rounded-2xl overflow-hidden" style={{
background: 'rgba(30, 35, 41, 0.4)',
border: '1px solid rgba(99, 102, 241, 0.2)',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.2)'
}}>
<div className="p-5 border-b" style={{ borderColor: 'rgba(99, 102, 241, 0.2)', background: 'rgba(30, 35, 41, 0.6)' }}>
<h3 className="font-bold flex items-center gap-2" style={{ color: '#E0E7FF' }}>
📊 Symbol Performance
</h3>
</div>
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr style={{ background: 'rgba(15, 23, 42, 0.6)' }}>
<th className="text-left px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Symbol</th>
<th className="text-right px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Trades</th>
<th className="text-right px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Win Rate</th>
<th className="text-right px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Total P&L</th>
<th className="text-right px-4 py-3 text-xs font-semibold" style={{ color: '#94A3B8' }}>Avg P&L</th>
</tr>
</thead>
<tbody>
{symbolStatsList.map((stat, idx) => (
<tr key={stat.symbol} className="transition-colors hover:bg-white/5" style={{
borderTop: idx > 0 ? '1px solid rgba(99, 102, 241, 0.1)' : 'none'
}}>
<td className="px-4 py-3">
<span className="font-bold mono text-sm" style={{ color: '#E0E7FF' }}>{stat.symbol}</span>
</td>
<td className="px-4 py-3 text-right mono text-sm" style={{ color: '#CBD5E1' }}>
{stat.total_trades}
</td>
<td className="px-4 py-3 text-right mono text-sm font-semibold" style={{
color: (stat.win_rate || 0) >= 50 ? '#10B981' : '#F87171'
}}>
{(stat.win_rate || 0).toFixed(1)}%
</td>
<td className="px-4 py-3 text-right mono text-sm font-bold" style={{
color: (stat.total_pn_l || 0) > 0 ? '#10B981' : '#F87171'
}}>
{(stat.total_pn_l || 0) > 0 ? '+' : ''}{(stat.total_pn_l || 0).toFixed(2)}%
</td>
<td className="px-4 py-3 text-right mono text-sm" style={{
color: (stat.avg_pn_l || 0) > 0 ? '#10B981' : '#F87171'
}}>
{(stat.avg_pn_l || 0) > 0 ? '+' : ''}{(stat.avg_pn_l || 0).toFixed(2)}%
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
{/* 中间列结束 */}
{/* 右侧列:历史成交记录 (3列) */}
<div className="lg:col-span-3">
<div className="rounded-2xl overflow-hidden sticky top-24" style={{
background: 'rgba(30, 35, 41, 0.4)',
border: '1px solid rgba(240, 185, 11, 0.2)',
maxHeight: 'calc(100vh - 200px)'
}}>
{/* 标题 - 固定在顶部 */}
<div className="p-4 border-b backdrop-blur-sm" style={{
background: 'rgba(240, 185, 11, 0.1)',
borderColor: 'rgba(240, 185, 11, 0.3)'
}}>
<div className="flex items-center gap-2">
<span className="text-xl">📜</span>
<div>
<h3 className="font-bold text-sm" style={{ color: '#FCD34D' }}>Trade History</h3>
<p className="text-xs" style={{ color: '#94A3B8' }}>
{performance?.recent_trades && performance.recent_trades.length > 0
? `Recent ${performance.recent_trades.length} completed trades`
: 'Completed trades will appear here'}
</p>
</div>
</div>
</div>
{/* 滚动内容区域 */}
<div className="overflow-y-auto p-4 space-y-3" style={{ maxHeight: 'calc(100vh - 300px)' }}>
{performance?.recent_trades && performance.recent_trades.length > 0 ? (
performance.recent_trades.map((trade: TradeOutcome, idx: number) => {
const isProfitable = trade.pn_l >= 0;
const isRecent = idx === 0;
return (
<div key={idx} className="rounded-xl p-4 backdrop-blur-sm transition-all hover:scale-[1.02]" style={{
background: isRecent
? isProfitable
? 'linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(14, 203, 129, 0.05) 100%)'
: 'linear-gradient(135deg, rgba(248, 113, 113, 0.15) 0%, rgba(246, 70, 93, 0.05) 100%)'
: 'rgba(30, 35, 41, 0.4)',
border: isRecent
? isProfitable ? '1px solid rgba(16, 185, 129, 0.4)' : '1px solid rgba(248, 113, 113, 0.4)'
: '1px solid rgba(71, 85, 105, 0.3)',
boxShadow: isRecent
? '0 4px 16px rgba(139, 92, 246, 0.2)'
: '0 2px 8px rgba(0, 0, 0, 0.1)'
}}>
{/* 头部:币种和方向 */}
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<span className="text-base font-bold mono" style={{ color: '#E0E7FF' }}>
{trade.symbol}
</span>
<span className="text-xs px-2 py-1 rounded font-bold" style={{
background: trade.side === 'long' ? 'rgba(14, 203, 129, 0.2)' : 'rgba(246, 70, 93, 0.2)',
color: trade.side === 'long' ? '#10B981' : '#F87171'
}}>
{trade.side.toUpperCase()}
</span>
{isRecent && (
<span className="text-xs px-2 py-0.5 rounded font-semibold" style={{
background: 'rgba(240, 185, 11, 0.2)',
color: '#FCD34D'
}}>
Latest
</span>
)}
</div>
<div className="text-lg font-bold mono" style={{
color: isProfitable ? '#10B981' : '#F87171'
}}>
{isProfitable ? '+' : ''}{trade.pn_l_pct.toFixed(2)}%
</div>
</div>
{/* 价格信息 */}
<div className="grid grid-cols-2 gap-2 mb-3 text-xs">
<div>
<div style={{ color: '#94A3B8' }}>Entry</div>
<div className="font-mono font-semibold" style={{ color: '#CBD5E1' }}>
{trade.open_price.toFixed(4)}
</div>
</div>
<div className="text-right">
<div style={{ color: '#94A3B8' }}>Exit</div>
<div className="font-mono font-semibold" style={{ color: '#CBD5E1' }}>
{trade.close_price.toFixed(4)}
</div>
</div>
</div>
{/* 盈亏详情 */}
<div className="rounded-lg p-2 mb-2" style={{
background: isProfitable ? 'rgba(16, 185, 129, 0.1)' : 'rgba(248, 113, 113, 0.1)'
}}>
<div className="flex items-center justify-between text-xs">
<span style={{ color: '#94A3B8' }}>P&L</span>
<span className="font-bold mono" style={{
color: isProfitable ? '#10B981' : '#F87171'
}}>
{isProfitable ? '+' : ''}{trade.pn_l.toFixed(2)} USDT
</span>
</div>
</div>
{/* 时间和持仓时长 */}
<div className="flex items-center justify-between text-xs" style={{ color: '#94A3B8' }}>
<span> {formatDuration(trade.duration)}</span>
{trade.was_stop_loss && (
<span className="px-2 py-0.5 rounded font-semibold" style={{
background: 'rgba(248, 113, 113, 0.2)',
color: '#FCA5A5'
}}>
Stop Loss
</span>
)}
</div>
{/* 交易时间 */}
<div className="text-xs mt-2 pt-2 border-t" style={{
color: '#64748B',
borderColor: 'rgba(71, 85, 105, 0.3)'
}}>
{new Date(trade.close_time).toLocaleString('en-US', {
month: 'short',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})}
</div>
</div>
);
})
) : (
<div className="p-6 text-center">
<div className="text-4xl mb-2 opacity-50">📜</div>
<div style={{ color: '#94A3B8' }}>No completed trades yet</div>
</div>
)}
</div>
</div>
</div>
{/* 右侧列结束 */}
</div>
{/* 3列布局结束 */}
{/* AI学习说明 - 现代化设计 */}
<div className="rounded-2xl p-6 backdrop-blur-sm" style={{
background: 'linear-gradient(135deg, rgba(240, 185, 11, 0.1) 0%, rgba(252, 213, 53, 0.05) 100%)',
border: '1px solid rgba(240, 185, 11, 0.2)',
boxShadow: '0 4px 16px rgba(240, 185, 11, 0.1)'
}}>
<div className="flex items-start gap-4">
<div className="w-10 h-10 rounded-lg flex items-center justify-center text-xl flex-shrink-0" style={{
background: 'rgba(240, 185, 11, 0.2)',
border: '1px solid rgba(240, 185, 11, 0.3)'
}}>
💡
</div>
<div>
<h3 className="font-bold mb-3 text-base" style={{ color: '#FCD34D' }}>How AI Learns & Evolves</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 text-sm">
<div className="flex items-start gap-2">
<span style={{ color: '#F0B90B' }}></span>
<span style={{ color: '#CBD5E1' }}>Analyzes last 20 trading cycles before each decision</span>
</div>
<div className="flex items-start gap-2">
<span style={{ color: '#F0B90B' }}></span>
<span style={{ color: '#CBD5E1' }}>Identifies best & worst performing symbols</span>
</div>
<div className="flex items-start gap-2">
<span style={{ color: '#F0B90B' }}></span>
<span style={{ color: '#CBD5E1' }}>Optimizes position sizing based on win rate</span>
</div>
<div className="flex items-start gap-2">
<span style={{ color: '#F0B90B' }}></span>
<span style={{ color: '#CBD5E1' }}>Avoids repeating past mistakes</span>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
// 格式化持仓时长
function formatDuration(duration: string | undefined): string {
if (!duration) return '-';
// duration格式: "1h23m45s" or "23m45.123s"
const match = duration.match(/(\d+h)?(\d+m)?(\d+\.?\d*s)?/);
if (!match) return duration;
const hours = match[1] || '';
const minutes = match[2] || '';
const seconds = match[3] || '';
let result = '';
if (hours) result += hours.replace('h', '小时');
if (minutes) result += minutes.replace('m', '分');
if (!hours && seconds) result += seconds.replace(/(\d+)\.?\d*s/, '$1秒');
return result || duration;
}
// 从CoT trace中提取AI的历史表现分析和反思
function extractReflectionFromCoT(cotTrace: string | undefined): string | null {
if (!cotTrace) return null;
// 优先提取【历史经验反思】部分(新格式)
const reflectionMatch = cotTrace.match(/【历史经验反思】\s*([\s\S]*?)(?=【|$)/);
if (reflectionMatch) {
const reflection = reflectionMatch[1].trim();
if (reflection.length > 50) {
return `🎯 AI历史经验总结\n\n${reflection}`;
}
}
// 尝试提取"历史表现反馈"部分(兼容旧格式)
const performanceSectionMatch = cotTrace.match(/## 📊 历史表现反馈[\s\S]*?(?=##|$)/);
if (performanceSectionMatch) {
const performanceSection = performanceSectionMatch[0];
// 提取关键学习点
const lines: string[] = [];
// 提取总体统计
const statsMatch = performanceSection.match(/(\d+).*?([\d.]+)%.*?([\d.]+)/s);
if (statsMatch) {
const [, totalTrades, winRate, profitFactor] = statsMatch;
lines.push(`📈 历史表现回顾:`);
lines.push(` • 完成了 ${totalTrades} 笔交易,胜率 ${winRate}%`);
lines.push(` • 盈亏比 ${profitFactor}(平均盈利/平均亏损)`);
lines.push('');
}
// 提取最近交易
const recentTradesMatch = performanceSection.match(/最近5笔交易[\s\S]*?(?=##|表现最好|$)/);
if (recentTradesMatch) {
const tradesText = recentTradesMatch[0];
const tradeLines = tradesText.split('\n').filter(line => line.trim().startsWith('-'));
if (tradeLines.length > 0) {
lines.push(`🔍 最近交易分析:`);
tradeLines.slice(0, 3).forEach(line => {
lines.push(` ${line.trim()}`);
});
lines.push('');
}
}
// 提取最佳/最差币种
const bestWorstMatch = performanceSection.match(/([A-Z]+).*?\((.*?)\).*?([A-Z]+).*?\((.*?)\)/s);
if (bestWorstMatch) {
const [, bestSymbol, bestPnl, worstSymbol, worstPnl] = bestWorstMatch;
lines.push(`💡 币种表现洞察:`);
lines.push(` 🏆 ${bestSymbol} 表现最佳 ${bestPnl}`);
lines.push(` 💔 ${worstSymbol} 表现较差 ${worstPnl}`);
lines.push('');
}
// 尝试提取AI的分析或决策理由
const analysisMatch = cotTrace.match(/(?:分析|策略|决策)[:]([\s\S]*?)(?:\n\n|##|$)/);
if (analysisMatch) {
const analysis = analysisMatch[1].trim();
if (analysis.length > 20 && analysis.length < 500) {
lines.push(`🎯 AI策略调整`);
lines.push(` ${analysis.substring(0, 300)}${analysis.length > 300 ? '...' : ''}`);
}
}
if (lines.length > 0) {
return lines.join('\n');
}
}
// 如果没有找到历史表现反馈,尝试提取整体思路
const thinkingMatch = cotTrace.match(/(?:思考|分析|策略)[:]([\s\S]{50,500}?)(?:\n\n|##|决策|$)/);
if (thinkingMatch) {
return `🤔 AI思考过程\n\n${thinkingMatch[1].trim().substring(0, 400)}...`;
}
// 如果都没有返回CoT的前面部分
if (cotTrace.length > 100) {
return `💭 AI分析摘要\n\n${cotTrace.substring(0, 400).trim()}...`;
}
return null;
}

View File

@@ -45,151 +45,155 @@ export function CompetitionPage() {
const leader = sortedTraders[0];
return (
<div className="space-y-6 animate-fade-in">
{/* Competition Header - Binance Style */}
<div className="rounded p-6 animate-scale-in" style={{ background: 'linear-gradient(135deg, rgba(240, 185, 11, 0.15) 0%, rgba(252, 213, 53, 0.05) 100%)', border: '1px solid rgba(240, 185, 11, 0.2)', boxShadow: '0 0 30px rgba(240, 185, 11, 0.15)' }}>
<div className="flex items-center justify-between">
<div className="space-y-5 animate-fade-in">
{/* Competition Header - 精简版 */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-xl flex items-center justify-center text-2xl" style={{
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
boxShadow: '0 4px 14px rgba(240, 185, 11, 0.4)'
}}>
🏆
</div>
<div>
<h1 className="text-2xl font-bold mb-1 flex items-center gap-3" style={{ color: '#EAECEF' }}>
<span>🏆</span>
AI Trading Competition
<span className="text-sm font-normal px-2 py-1 rounded" style={{ background: 'rgba(240, 185, 11, 0.15)', color: '#F0B90B' }}>
{competition.count} Traders
<h1 className="text-2xl font-bold flex items-center gap-2" style={{ color: '#EAECEF' }}>
AI Competition
<span className="text-xs font-normal px-2 py-1 rounded" style={{ background: 'rgba(240, 185, 11, 0.15)', color: '#F0B90B' }}>
{competition.count} traders
</span>
</h1>
<p className="text-sm" style={{ color: '#848E9C' }}>
Qwen vs DeepSeek · Real-time Performance Battle
<p className="text-xs" style={{ color: '#848E9C' }}>
Qwen vs DeepSeek · Live Battle
</p>
</div>
<div className="text-right">
<div className="text-xs" style={{ color: '#848E9C' }}>Current Leader</div>
<div className="text-lg font-bold" style={{ color: '#F0B90B' }}>{leader?.trader_name}</div>
</div>
<div className="text-right">
<div className="text-xs mb-1" style={{ color: '#848E9C' }}>🥇 Leader</div>
<div className="text-lg font-bold" style={{ color: '#F0B90B' }}>{leader?.trader_name}</div>
<div className="text-sm font-semibold" style={{ color: leader.total_pnl >= 0 ? '#0ECB81' : '#F6465D' }}>
{leader.total_pnl >= 0 ? '+' : ''}{leader.total_pnl_pct.toFixed(2)}%
</div>
</div>
</div>
{/* Leader Board - Binance Style */}
<div className="binance-card p-6 animate-slide-in" style={{ animationDelay: '0.1s' }}>
<div className="flex items-center justify-between mb-5">
<h2 className="text-xl font-bold flex items-center gap-2" style={{ color: '#EAECEF' }}>
🥇 Leaderboard
</h2>
<div className="text-xs px-3 py-1 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', color: '#F0B90B', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
LIVE
{/* Left/Right Split: Performance Chart + Leaderboard */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
{/* Left: Performance Comparison Chart */}
<div className="binance-card p-5 animate-slide-in" style={{ animationDelay: '0.1s' }}>
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-bold flex items-center gap-2" style={{ color: '#EAECEF' }}>
📈 Performance Comparison
</h2>
<div className="text-xs" style={{ color: '#848E9C' }}>
Real-time PnL %
</div>
</div>
<ComparisonChart traders={sortedTraders} />
</div>
<div className="space-y-3">
{sortedTraders.map((trader, index) => {
const isLeader = index === 0;
const aiModelColor = trader.ai_model === 'qwen' ? '#c084fc' : '#60a5fa';
return (
<div
key={trader.trader_id}
className="rounded p-4 transition-all duration-300 hover:translate-y-[-2px]"
style={{
background: isLeader ? 'linear-gradient(135deg, rgba(240, 185, 11, 0.08) 0%, #0B0E11 100%)' : '#0B0E11',
border: `1px solid ${isLeader ? 'rgba(240, 185, 11, 0.4)' : '#2B3139'}`,
boxShadow: isLeader ? '0 4px 20px rgba(240, 185, 11, 0.15), 0 0 0 1px rgba(240, 185, 11, 0.2)' : '0 2px 8px rgba(0, 0, 0, 0.3)'
}}
>
<div className="flex items-center justify-between">
{/* Rank & Name */}
<div className="flex items-center gap-4">
<div className="text-3xl w-8">
{index === 0 ? '🥇' : index === 1 ? '🥈' : '🥉'}
</div>
<div>
<div className="font-bold text-base" style={{ color: '#EAECEF' }}>{trader.trader_name}</div>
<div className="text-xs mono font-semibold" style={{ color: aiModelColor }}>
{trader.ai_model.toUpperCase()} Model
{/* Right: Leaderboard */}
<div className="binance-card p-5 animate-slide-in" style={{ animationDelay: '0.1s' }}>
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-bold flex items-center gap-2" style={{ color: '#EAECEF' }}>
🥇 Leaderboard
</h2>
<div className="text-xs px-2 py-1 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', color: '#F0B90B', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
LIVE
</div>
</div>
<div className="space-y-2">
{sortedTraders.map((trader, index) => {
const isLeader = index === 0;
const aiModelColor = trader.ai_model === 'qwen' ? '#c084fc' : '#60a5fa';
return (
<div
key={trader.trader_id}
className="rounded p-3 transition-all duration-300 hover:translate-y-[-1px]"
style={{
background: isLeader ? 'linear-gradient(135deg, rgba(240, 185, 11, 0.08) 0%, #0B0E11 100%)' : '#0B0E11',
border: `1px solid ${isLeader ? 'rgba(240, 185, 11, 0.4)' : '#2B3139'}`,
boxShadow: isLeader ? '0 3px 15px rgba(240, 185, 11, 0.12), 0 0 0 1px rgba(240, 185, 11, 0.15)' : '0 1px 4px rgba(0, 0, 0, 0.3)'
}}
>
<div className="flex items-center justify-between">
{/* Rank & Name */}
<div className="flex items-center gap-3">
<div className="text-2xl w-6">
{index === 0 ? '🥇' : index === 1 ? '🥈' : '🥉'}
</div>
</div>
</div>
{/* Stats */}
<div className="flex items-center gap-6">
{/* Total Equity */}
<div className="text-right">
<div className="text-xs" style={{ color: '#848E9C' }}>Total Equity</div>
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>
{trader.total_equity.toFixed(2)} USDT
<div>
<div className="font-bold text-sm" style={{ color: '#EAECEF' }}>{trader.trader_name}</div>
<div className="text-xs mono font-semibold" style={{ color: aiModelColor }}>
{trader.ai_model.toUpperCase()}
</div>
</div>
</div>
{/* P&L */}
<div className="text-right min-w-[120px]">
<div className="text-xs" style={{ color: '#848E9C' }}>Total P&L</div>
<div
className="text-2xl font-bold mono"
style={{ color: trader.total_pnl >= 0 ? '#0ECB81' : '#F6465D' }}
>
{trader.total_pnl >= 0 ? '+' : ''}
{trader.total_pnl_pct.toFixed(2)}%
{/* Stats */}
<div className="flex items-center gap-3">
{/* Total Equity */}
<div className="text-right">
<div className="text-xs" style={{ color: '#848E9C' }}>Equity</div>
<div className="text-sm font-bold mono" style={{ color: '#EAECEF' }}>
{trader.total_equity.toFixed(2)}
</div>
</div>
<div className="text-xs mono" style={{ color: '#848E9C' }}>
{trader.total_pnl >= 0 ? '+' : ''}
{trader.total_pnl.toFixed(2)} USDT
</div>
</div>
{/* Positions */}
<div className="text-right">
<div className="text-xs" style={{ color: '#848E9C' }}>Positions</div>
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>
{trader.position_count}
{/* P&L */}
<div className="text-right min-w-[90px]">
<div className="text-xs" style={{ color: '#848E9C' }}>P&L</div>
<div
className="text-lg font-bold mono"
style={{ color: trader.total_pnl >= 0 ? '#0ECB81' : '#F6465D' }}
>
{trader.total_pnl >= 0 ? '+' : ''}
{trader.total_pnl_pct.toFixed(2)}%
</div>
<div className="text-xs mono" style={{ color: '#848E9C' }}>
{trader.total_pnl >= 0 ? '+' : ''}{trader.total_pnl.toFixed(2)}
</div>
</div>
<div className="text-xs" style={{ color: '#848E9C' }}>
Margin: {trader.margin_used_pct.toFixed(1)}%
{/* Positions */}
<div className="text-right">
<div className="text-xs" style={{ color: '#848E9C' }}>Pos</div>
<div className="text-sm font-bold mono" style={{ color: '#EAECEF' }}>
{trader.position_count}
</div>
<div className="text-xs" style={{ color: '#848E9C' }}>
{trader.margin_used_pct.toFixed(1)}%
</div>
</div>
</div>
{/* Cycles */}
<div className="text-right">
<div className="text-xs" style={{ color: '#848E9C' }}>Cycles</div>
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>{trader.call_count}</div>
</div>
{/* Status */}
<div>
<div
className="px-3 py-1 rounded text-xs font-bold"
style={trader.is_running
? { background: 'rgba(14, 203, 129, 0.1)', color: '#0ECB81' }
: { background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }
}
>
{trader.is_running ? 'RUNNING' : 'STOPPED'}
{/* Status */}
<div>
<div
className="px-2 py-1 rounded text-xs font-bold"
style={trader.is_running
? { background: 'rgba(14, 203, 129, 0.1)', color: '#0ECB81' }
: { background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }
}
>
{trader.is_running ? '●' : '○'}
</div>
</div>
</div>
</div>
</div>
</div>
);
})}
</div>
</div>
{/* Performance Comparison Chart */}
<div className="binance-card p-6 animate-slide-in" style={{ animationDelay: '0.2s' }}>
<div className="flex items-center justify-between mb-5">
<h2 className="text-xl font-bold flex items-center gap-2" style={{ color: '#EAECEF' }}>
📈 Performance Comparison
</h2>
<div className="text-xs" style={{ color: '#848E9C' }}>
Real-time PnL % Chart
);
})}
</div>
</div>
<ComparisonChart traders={sortedTraders} />
</div>
{/* Head-to-Head Stats */}
{competition.traders.length === 2 && (
<div className="binance-card p-6 animate-slide-in" style={{ animationDelay: '0.3s' }}>
<h2 className="text-xl font-bold mb-5 flex items-center gap-2" style={{ color: '#EAECEF' }}>
<div className="binance-card p-5 animate-slide-in" style={{ animationDelay: '0.3s' }}>
<h2 className="text-lg font-bold mb-4 flex items-center gap-2" style={{ color: '#EAECEF' }}>
Head-to-Head Battle
</h2>
<div className="grid grid-cols-2 gap-6">
<div className="grid grid-cols-2 gap-4">
{sortedTraders.map((trader, index) => {
const isWinning = index === 0;
const opponent = sortedTraders[1 - index];
@@ -198,37 +202,37 @@ export function CompetitionPage() {
return (
<div
key={trader.trader_id}
className="p-6 rounded transition-all duration-300 hover:scale-105"
className="p-4 rounded transition-all duration-300 hover:scale-[1.02]"
style={isWinning
? {
background: 'linear-gradient(135deg, rgba(14, 203, 129, 0.08) 0%, rgba(14, 203, 129, 0.02) 100%)',
border: '2px solid rgba(14, 203, 129, 0.3)',
boxShadow: '0 4px 20px rgba(14, 203, 129, 0.15)'
boxShadow: '0 3px 15px rgba(14, 203, 129, 0.12)'
}
: {
background: '#0B0E11',
border: '1px solid #2B3139',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)'
boxShadow: '0 1px 4px rgba(0, 0, 0, 0.3)'
}
}
>
<div className="text-center">
<div
className="text-lg font-bold mb-2"
className="text-base font-bold mb-2"
style={{ color: trader.ai_model === 'qwen' ? '#c084fc' : '#60a5fa' }}
>
{trader.trader_name}
</div>
<div className="text-3xl font-bold mono mb-2" style={{ color: trader.total_pnl >= 0 ? '#0ECB81' : '#F6465D' }}>
<div className="text-2xl font-bold mono mb-1" style={{ color: trader.total_pnl >= 0 ? '#0ECB81' : '#F6465D' }}>
{trader.total_pnl >= 0 ? '+' : ''}{trader.total_pnl_pct.toFixed(2)}%
</div>
{isWinning && gap > 0 && (
<div className="text-sm font-semibold" style={{ color: '#0ECB81' }}>
<div className="text-xs font-semibold" style={{ color: '#0ECB81' }}>
Leading by {gap.toFixed(2)}%
</div>
)}
{!isWinning && gap < 0 && (
<div className="text-sm font-semibold" style={{ color: '#F6465D' }}>
<div className="text-xs font-semibold" style={{ color: '#F6465D' }}>
Behind by {Math.abs(gap).toFixed(2)}%
</div>
)}

View File

@@ -149,9 +149,9 @@ export function EquityChart({ traderId }: EquityChartProps) {
};
return (
<div className="binance-card p-6 animate-fade-in">
<div className="binance-card p-5 animate-fade-in">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="text-lg font-bold mb-2" style={{ color: '#EAECEF' }}>线</h3>
<div className="flex items-baseline gap-4">
@@ -205,8 +205,8 @@ export function EquityChart({ traderId }: EquityChartProps) {
{/* Chart */}
<div className="my-2" style={{ borderRadius: '8px', overflow: 'hidden' }}>
<ResponsiveContainer width="100%" height={420}>
<LineChart data={chartData} margin={{ top: 20, right: 30, left: 10, bottom: 40 }}>
<ResponsiveContainer width="100%" height={280}>
<LineChart data={chartData} margin={{ top: 10, right: 20, left: 5, bottom: 30 }}>
<defs>
<linearGradient id="colorGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#F0B90B" stopOpacity={0.8} />
@@ -257,26 +257,26 @@ export function EquityChart({ traderId }: EquityChartProps) {
</div>
{/* Footer Stats */}
<div className="mt-5 grid grid-cols-4 gap-4 pt-5" style={{ borderTop: '1px solid #2B3139' }}>
<div className="p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
<div className="mt-3 grid grid-cols-4 gap-3 pt-3" style={{ borderTop: '1px solid #2B3139' }}>
<div className="p-2 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
<div className="text-xs mb-1 uppercase tracking-wider" style={{ color: '#848E9C' }}></div>
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>
<div className="text-sm font-bold mono" style={{ color: '#EAECEF' }}>
{initialBalance.toFixed(2)} USDT
</div>
</div>
<div className="p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
<div className="p-2 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
<div className="text-xs mb-1 uppercase tracking-wider" style={{ color: '#848E9C' }}></div>
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>
<div className="text-sm font-bold mono" style={{ color: '#EAECEF' }}>
{currentValue.raw_equity.toFixed(2)} USDT
</div>
</div>
<div className="p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
<div className="p-2 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
<div className="text-xs mb-1 uppercase tracking-wider" style={{ color: '#848E9C' }}></div>
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>{history.length} </div>
<div className="text-sm font-bold mono" style={{ color: '#EAECEF' }}>{history.length} </div>
</div>
<div className="p-3 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
<div className="p-2 rounded transition-all hover:bg-opacity-50" style={{ background: 'rgba(240, 185, 11, 0.05)' }}>
<div className="text-xs mb-1 uppercase tracking-wider" style={{ color: '#848E9C' }}></div>
<div className="text-base font-bold mono" style={{ color: '#EAECEF' }}>
<div className="text-sm font-bold mono" style={{ color: '#EAECEF' }}>
{history.length > MAX_DISPLAY_POINTS
? `最近 ${MAX_DISPLAY_POINTS}`
: '全部数据'