mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-02 18:41:01 +08:00
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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,3 +27,4 @@ config.json
|
||||
# 决策日志
|
||||
decision_logs/
|
||||
coin_pool_cache/
|
||||
nofx-auto
|
||||
|
||||
655
README.md
655
README.md
@@ -1,140 +1,136 @@
|
||||
# 🤖 NOFX - AI-Driven Binance Futures Auto Trading Competition System
|
||||
# 🤖 NOFX - AI驱动的币安合约自动交易竞赛系统
|
||||
|
||||
[](https://golang.org/)
|
||||
[](https://reactjs.org/)
|
||||
[](https://www.typescriptlang.org/)
|
||||
[](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 │
|
||||
│ • 获取每个持仓的市场数据 │
|
||||
│ • 计算技术指标(RSI、MACD、EMA) │
|
||||
│ • 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失败,系统会使用默认主流币种(BTC、ETH等)
|
||||
- 检查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 Top前20
|
||||
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!
|
||||
欢迎提交Issue和Pull 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的力量,探索量化交易的可能性!**
|
||||
|
||||
@@ -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
84
config.example.go
Normal 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白名单
|
||||
// ⚠️ 定期检查持仓和盈亏
|
||||
// ⚠️ 加密货币交易有风险,投资需谨慎
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
122
web/src/App.tsx
122
web/src/App.tsx
@@ -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>
|
||||
);
|
||||
|
||||
688
web/src/components/AILearning.tsx
Normal file
688
web/src/components/AILearning.tsx
Normal 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;
|
||||
}
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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}`
|
||||
: '全部数据'
|
||||
|
||||
Reference in New Issue
Block a user