Resolve merge conflicts in AITradersPage.tsx

- Fixed import statement conflict (using 'type Language')
- Merged exchange configuration logic preserving support for multiple exchange types
- Kept comprehensive form handling for Binance, Hyperliquid, Aster, and OKX exchanges
- Updated security warning messages to use proper translation keys
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
icy
2025-11-01 19:01:44 +08:00
15 changed files with 753 additions and 381 deletions

152
README.md
View File

@@ -1,16 +1,43 @@
# 🤖 NOFX - Multi-AI Model Automated Trading Platform
# 🤖 NOFX - Agentic Trading OS
[![Go Version](https://img.shields.io/badge/Go-1.21+-00ADD8?style=flat&logo=go)](https://golang.org/)
[![React](https://img.shields.io/badge/React-18+-61DAFB?style=flat&logo=react)](https://reactjs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?style=flat&logo=typescript)](https://www.typescriptlang.org/)
[![SQLite](https://img.shields.io/badge/SQLite-3+-003B57?style=flat&logo=sqlite)](https://sqlite.org/)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Backed by Amber.ac](https://img.shields.io/badge/Backed%20by-Amber.ac-orange.svg)](https://amber.ac)
**Languages:** [English](README.md) | [中文](README.zh-CN.md) | [Українська](README.uk.md) | [Русский](README.ru.md)
**Official Twitter:** [@nofx_ai](https://x.com/nofx_ai)
---
A modern automated crypto futures trading platform powered by **DeepSeek/Qwen AI**, supporting **Binance, Hyperliquid, and Aster DEX exchanges**. Create and manage multiple AI traders with dynamic configuration through a web interface. Features comprehensive market analysis, AI decision-making, **multi-AI model live trading competition**, **self-learning mechanism**, and professional monitoring dashboard.
## 🚀 Universal AI Trading Operating System
**NOFX** is a **universal Agentic Trading OS** built on a unified architecture. We've successfully closed the loop in crypto markets: **"Multi-Agent Decision → Unified Risk Control → Low-Latency Execution → Live/Paper Account Backtesting"**, and are now expanding this same technology stack to **stocks, futures, options, forex, and all financial markets**.
### 🎯 Core Features
- **Universal Data & Backtesting Layer**: Cross-market, cross-timeframe, cross-exchange unified representation and factor library, accumulating transferable "strategy memory"
- **Multi-Agent Self-Play & Self-Evolution**: Strategies automatically compete and select the best, continuously iterating based on account-level PnL and risk constraints
- **Integrated Execution & Risk Control**: Low-latency routing, slippage/risk control sandbox, account-level limits, one-click market switching
### 🏢 Backed by [Amber.ac](https://amber.ac)
### 👥 Core Team
- **Tinkle** - [@Web3Tinkle](https://x.com/Web3Tinkle)
- **Zack** - [@0x_ZackH](https://x.com/0x_ZackH)
### 💼 Seed Funding Round Open
We are currently raising our **seed round**.
**For investment inquiries**, please DM **Tinkle** or **Zack** via Twitter.
**For partnerships and collaborations**, please DM our official Twitter [@nofx_ai](https://x.com/nofx_ai).
---
> ⚠️ **Risk Warning**: This system is experimental. AI auto-trading carries significant risks. Strongly recommended for learning/research purposes or testing with small amounts only!
@@ -24,11 +51,7 @@ Join our Telegram developer community to discuss, share ideas, and get support:
## 🆕 What's New (Latest Update)
### 🚀 Complete System Transformation - Web-Based Configuration!
NOFX has been **completely transformed** from a static config-based system to a **dynamic web-based trading platform** with **multi-exchange support**!
#### **Multi-Exchange Support**
### 🚀 Multi-Exchange Support!
NOFX now supports **three major exchanges**: Binance, Hyperliquid, and Aster DEX!
@@ -36,13 +59,12 @@ NOFX now supports **three major exchanges**: Binance, Hyperliquid, and Aster DEX
A high-performance decentralized perpetual futures exchange!
**Major Changes:**
-**Web-Based Configuration**: Create and manage AI traders through a modern web interface
-**Database-Driven Architecture**: SQLite database replaces static JSON configuration
-**Separate AI Models & Exchanges**: Configure AI models and exchanges independently
-**Dynamic Trader Creation**: Create traders by combining configured AI models and exchanges
-**Real-Time Management**: Start/stop traders, update configurations without restart
-**No Default Traders**: Clean slate - create only the traders you need
**Key Features:**
-Full trading support (long/short, leverage, stop-loss/take-profit)
-Automatic precision handling (order size & price)
-Unified trader interface (seamless exchange switching)
-Support for both mainnet and testnet
-No API keys needed - just your Ethereum private key
**New Workflow:**
1. **Configure AI Models**: Add your DeepSeek/Qwen API keys through the web interface
@@ -96,58 +118,64 @@ A Binance-compatible decentralized perpetual futures exchange!
---
## ✨ Core Features
## ✨ Current Implementation - Crypto Markets
### 🎛️ Web-Based Configuration Management
- **Dynamic AI Model Setup**: Configure DeepSeek and Qwen API keys through web interface
- **Exchange Management**: Set up Binance and Hyperliquid credentials independently
- **Flexible Trader Creation**: Mix any AI model with any exchange
- **Real-Time Control**: Start/stop traders without system restart
NOFX is currently **fully operational in cryptocurrency markets** with the following proven capabilities:
### 🧠 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)
### 🏆 Multi-Agent Competition Framework
- **Live Agent Battle**: Qwen vs DeepSeek models compete in real-time trading
- **Independent Account Management**: Each agent maintains its own decision logs and performance metrics
- **Real-time Performance Comparison**: Live ROI tracking, win rate statistics, and head-to-head analysis
- **Self-Evolution Loop**: Agents learn from their historical performance and continuously improve
### 🧠 AI Self-Learning & Optimization
- **Historical Feedback System**: Analyzes last 20 trading cycles before each decision
- **Smart Performance Analysis**:
- Identifies best/worst performing assets
- Calculates win rate, profit/loss ratio, average profit in real USDT terms
- Avoids repeating mistakes (consecutive losing patterns)
- Reinforces successful strategies (high win rate patterns)
- **Dynamic Adjustment**: AI autonomously adjusts trading style based on historical performance
- **Dynamic Strategy Adjustment**: AI autonomously adapts trading style based on backtest results
### 📊 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)
### 📊 Universal Market Data Layer (Crypto Implementation)
- **Multi-Timeframe Analysis**: 3-minute real-time + 4-hour trend data
- **Technical Indicators**: EMA20/50, MACD, RSI(7/14), ATR
- **Open Interest Tracking**: Market sentiment, capital flow analysis
- **Liquidity Filtering**: Auto-filters low liquidity assets (<15M USD)
- **Cross-Exchange Support**: Binance, Hyperliquid, Aster DEX with unified data interface
### 🎯 Professional Risk Control
- **Per-Coin Position Limit**:
- Altcoins 1.5x account equity
- BTC/ETH 10x account equity
- **Configurable Leverage** (v2.0.3+):
- Set maximum leverage in config.json
- Default: 5x for all coins (safe for subaccounts)
- Main accounts can increase: Altcoins up to 20x, BTC/ETH up to 50x
- Binance subaccounts restricted to 5x leverage
- **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
### 🎯 Unified Risk Control System
- **Position Limits**: Per-asset limits (Altcoins 1.5x equity, BTC/ETH 10x equity)
- **Configurable Leverage**: Dynamic leverage from 1x to 50x based on asset class and account type
- **Margin Management**: Total usage 90%, AI-controlled allocation
- **Risk-Reward Enforcement**: Mandatory 1:2 stop-loss to take-profit ratio
- **Anti-Stacking Protection**: Prevents duplicate positions in same asset/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
### ⚡ Low-Latency Execution Engine
- **Multi-Exchange API Integration**: Binance Futures, Hyperliquid DEX, Aster DEX
- **Automatic Precision Handling**: Smart order size & price formatting per exchange
- **Priority Execution**: Close existing positions first, then open new ones
- **Slippage Control**: Pre-execution validation, real-time precision checks
### 📝 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
### 🎨 Professional Monitoring Interface
- **Binance-Style Dashboard**: Professional dark theme with real-time updates
- **Equity Curves**: Historical account value tracking (USD/percentage toggle)
- **Performance Charts**: Multi-agent ROI comparison with live updates
- **Complete Decision Logs**: Full Chain of Thought (CoT) reasoning for every trade
- **5-Second Data Refresh**: Real-time account, position, and P/L updates
---
## 🔮 Roadmap - Universal Market Expansion
Our proven crypto infrastructure is being extended to:
- **📈 Stock Markets**: US equities, A-shares, Hong Kong stocks
- **📊 Futures Markets**: Commodity futures, index futures
- **🎯 Options Trading**: Equity options, crypto options
- **💱 Forex Markets**: Major currency pairs, cross rates
**Same architecture. Same agent framework. All markets.**
---
@@ -1365,8 +1393,10 @@ Issues and Pull Requests are welcome!
## 📬 Contact
- **Twitter/X**: [@Web3Tinkle](https://x.com/Web3Tinkle)
### 🐛 Technical Support
- **GitHub Issues**: [Submit an Issue](https://github.com/tinkle-community/nofx/issues)
- **Developer Community**: [Telegram Group](https://t.me/nofx_dev_community)
---

View File

@@ -1,15 +1,43 @@
# 🤖 NOFX - AI-управляемая система автоматической торговли фьючерсами Binance
# 🤖 NOFX - Agentic Trading OS
[![Go Version](https://img.shields.io/badge/Go-1.21+-00ADD8?style=flat&logo=go)](https://golang.org/)
[![React](https://img.shields.io/badge/React-18+-61DAFB?style=flat&logo=react)](https://reactjs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?style=flat&logo=typescript)](https://www.typescriptlang.org/)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Backed by Amber.ac](https://img.shields.io/badge/Backed%20by-Amber.ac-orange.svg)](https://amber.ac)
**Языки / Languages:** [English](README.md) | [中文](README.zh-CN.md) | [Українська](README.uk.md) | [Русский](README.ru.md)
**Официальный Twitter:** [@nofx_ai](https://x.com/nofx_ai)
---
Автоматизированная система торговли криптовалютными фьючерсами на базе **DeepSeek/Qwen AI**, поддерживающая **Binance, Hyperliquid и Aster DEX биржи**, **конкуренцию нескольких AI-моделей в реальной торговле**, с полным анализом рынка, принятием решений AI, **механизмом самообучения** и профессиональным веб-интерфейсом мониторинга.
## 🚀 Универсальная AI Торговая Операционная Система
**NOFX** - это **универсальная Agentic Trading OS**, построенная на единой архитектуре. Мы успешно замкнули цикл на криптовалютных рынках: **"Решение Multi-Agent → Единый Контроль Рисков → Исполнение с Низкой Задержкой → Бэктестинг Реальных/Бумажных Счетов"**, и сейчас расширяем этот же технологический стек на **акции, фьючерсы, опционы, форекс и все финансовые рынки**.
### 🎯 Основные Возможности
- **Универсальный Слой Данных и Бэктестинга**: Кросс-рыночное, кросс-таймфреймовое, кросс-биржевое единое представление и библиотека факторов, накапливающая переносимую "память стратегий"
- **Multi-Agent Самоигра и Самоэволюция**: Стратегии автоматически конкурируют и выбирают лучшие, непрерывно итерируясь на основе PnL на уровне счета и ограничений рисков
- **Интегрированное Исполнение и Контроль Рисков**: Маршрутизация с низкой задержкой, песочница проскальзывания/контроля рисков, лимиты на уровне счета, переключение рынков одним кликом
### 🏢 При поддержке [Amber.ac](https://amber.ac)
### 👥 Основная Команда
- **Tinkle** - [@Web3Tinkle](https://x.com/Web3Tinkle)
- **Zack** - [@0x_ZackH](https://x.com/0x_ZackH)
### 💼 Открыт Посевной Раунд Финансирования
Мы в настоящее время привлекаем **посевной раунд**.
**По вопросам инвестиций**, пишите в DM **Tinkle** или **Zack** в Twitter.
**По вопросам партнерства и сотрудничества**, пишите в DM нашего официального Twitter [@nofx_ai](https://x.com/nofx_ai).
---
> ⚠️ **Предупреждение о рисках**: Эта система экспериментальная. Автоматическая торговля с AI несет значительные риски. Настоятельно рекомендуется использовать только для обучения/исследований или тестирования с небольшими суммами!

View File

@@ -1,15 +1,43 @@
# 🤖 NOFX - AI-керована система автоматичної торгівлі ф'ючерсами Binance
# 🤖 NOFX - Agentic Trading OS
[![Go Version](https://img.shields.io/badge/Go-1.21+-00ADD8?style=flat&logo=go)](https://golang.org/)
[![React](https://img.shields.io/badge/React-18+-61DAFB?style=flat&logo=react)](https://reactjs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?style=flat&logo=typescript)](https://www.typescriptlang.org/)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Backed by Amber.ac](https://img.shields.io/badge/Backed%20by-Amber.ac-orange.svg)](https://amber.ac)
**Мови / Languages:** [English](README.md) | [中文](README.zh-CN.md) | [Українська](README.uk.md) | [Русский](README.ru.md)
**Офіційний Twitter:** [@nofx_ai](https://x.com/nofx_ai)
---
Автоматизована система торгівлі криптовалютними ф'ючерсами на базі **DeepSeek/Qwen AI**, що підтримує **Binance, Hyperliquid та Aster DEX біржі**, **змагання кількох AI-моделей у реальній торгівлі**, з повним аналізом ринку, прийняттям рішень AI, **механізмом самонавчання** та професійним веб-інтерфейсом моніторингу.
## 🚀 Універсальна AI Торгова Операційна Система
**NOFX** - це **універсальна Agentic Trading OS**, побудована на єдиній архітектурі. Ми успішно замкнули цикл на криптовалютних ринках: **"Рішення Multi-Agent → Єдиний Контроль Ризиків → Виконання з Низькою Затримкою → Бектестинг Реальних/Паперових Рахунків"**, і зараз розширюємо цей же технологічний стек на **акції, ф'ючерси, опціони, форекс та всі фінансові ринки**.
### 🎯 Основні Можливості
- **Універсальний Шар Даних та Бектестингу**: Крос-ринкове, крос-таймфреймове, крос-біржеве єдине представлення та бібліотека факторів, що накопичує переносиму "пам'ять стратегій"
- **Multi-Agent Самогра та Самоеволюція**: Стратегії автоматично конкурують і вибирають кращі, безперервно ітеруючись на основі PnL на рівні рахунку та обмежень ризиків
- **Інтегроване Виконання та Контроль Ризиків**: Маршрутизація з низькою затримкою, пісочниця прослизання/контролю ризиків, ліміти на рівні рахунку, перемикання ринків одним кліком
### 🏢 За підтримки [Amber.ac](https://amber.ac)
### 👥 Основна Команда
- **Tinkle** - [@Web3Tinkle](https://x.com/Web3Tinkle)
- **Zack** - [@0x_ZackH](https://x.com/0x_ZackH)
### 💼 Відкритий Посівний Раунд Фінансування
Ми зараз залучаємо **посівний раунд**.
**З питань інвестицій**, пишіть в DM **Tinkle** або **Zack** в Twitter.
**З питань партнерства та співпраці**, пишіть в DM нашого офіційного Twitter [@nofx_ai](https://x.com/nofx_ai).
---
> ⚠️ **Попередження про ризики**: Ця система експериментальна. Автоматична торгівля з AI несе значні ризики. Наполегливо рекомендується використовувати лише для навчання/досліджень або тестування з невеликими сумами!

View File

@@ -1,15 +1,43 @@
# 🤖 NOFX - AI驱动的币安合约自动交易竞赛系统
# 🤖 NOFX - AI交易操作系统
[![Go Version](https://img.shields.io/badge/Go-1.21+-00ADD8?style=flat&logo=go)](https://golang.org/)
[![React](https://img.shields.io/badge/React-18+-61DAFB?style=flat&logo=react)](https://reactjs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?style=flat&logo=typescript)](https://www.typescriptlang.org/)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Backed by Amber.ac](https://img.shields.io/badge/Backed%20by-Amber.ac-orange.svg)](https://amber.ac)
**语言 / Languages:** [English](README.md) | [中文](README.zh-CN.md) | [Українська](README.uk.md) | [Русский](README.ru.md)
**官方推特:** [@nofx_ai](https://x.com/nofx_ai)
---
一个基于 **DeepSeek/Qwen AI** 的加密货币期货自动交易系统,支持 **Binance、Hyperliquid和Aster DEX交易所****多AI模型实盘竞赛**具备完整的市场分析、AI决策、**自我学习机制**和专业的Web监控界面。
## 🚀 通用AI交易操作系统
**NOFX** 是通用架构的 **AI交易操作系统Agentic Trading OS**。我们已在加密市场打通"**多智能体决策 → 统一风控 → 低延迟执行 → 真实/纸面账户复盘**"的闭环,正按同一技术栈扩展到**股票、期货、期权、外汇等所有市场**。
### 🎯 核心特性
- **通用数据与回测层**:跨市场、跨周期、跨交易所统一表示与因子库,沉淀可迁移的"策略记忆"
- **多智能体自博弈与自进化**:策略自动对战择优,按账户级 PnL 与风险约束持续迭代
- **执行与风控一体化**:低延迟路由、滑点/风控沙箱、账户级限额,一键切换市场
### 🏢 由 [Amber.ac](https://amber.ac) 背书
### 👥 核心团队
- **Tinkle** - [@Web3Tinkle](https://x.com/Web3Tinkle)
- **Zack** - [@0x_ZackH](https://x.com/0x_ZackH)
### 💼 种子轮融资进行中
我们正在进行**种子轮融资**。
**投资咨询**,请通过 Twitter 私信联系 **Tinkle****Zack**
**商务合作**,请私信官方推特 [@nofx_ai](https://x.com/nofx_ai)。
---
> ⚠️ **风险提示**本系统为实验性项目AI自动交易存在重大风险强烈建议仅用于学习研究或小额资金测试
@@ -90,58 +118,64 @@ NOFX现已支持**三大交易所**Binance、Hyperliquid和Aster DEX
---
## ✨ 核心特性
## ✨ 当前实现 - 加密货币市场
### 🏆 多AI竞赛模式
- **Qwen vs DeepSeek** 实盘对抗
- 独立账户管理,独立决策日志
- 实时性能对比图表
- 收益率PK胜率统计
NOFX 目前已在**加密货币市场全面运行**,具备以下经过验证的能力:
### 🧠 AI自我学习机制NEW
- **历史反馈**: 每次决策前分析最近20个周期的交易表现
- **智能优化**:
- 识别表现最佳/最差币种
- 计算胜率、盈亏比、平均盈利
- 避免重复错误(连续亏损的币种)
- 强化成功策略(高胜率的交易模式)
- **动态调整**: AI根据历史表现自主调整交易风格
### 🏆 多智能体竞赛框架
- **实时智能体对战**Qwen vs DeepSeek 模型实时交易竞赛
- **独立账户管理**:每个智能体维护独立的决策日志和性能指标
- **实时性能对比**:实时 ROI 追踪、胜率统计、正面对抗分析
- **自进化循环**:智能体从历史表现中学习,持续改进
### 📊 智能市场分析
- **3分钟K线**: 实时价格、EMA20、MACD、RSI(7)
- **4小时K线**: 长期趋势、EMA20/50、ATR、RSI(14)
- **持仓量分析**: 市场情绪、资金流向判断
- **OI Top追踪**: 持仓量增长最快的20个币种
- **AI500币种池**: 高评分币种自动筛选
- **流动性过滤**: 自动过滤持仓价值<15M USD的低流动性币种
### 🧠 AI 自学习与优化
- **历史反馈系统**:每次决策前分析最近 20 个交易周期
- **智能性能分析**
- 识别表现最佳/最差资产
- 计算胜率、盈亏比、以真实 USDT 计的平均盈利
- 避免重复错误(连续亏损模式)
- 强化成功策略(高胜率模式)
- **动态策略调整**AI 根据回测结果自主调整交易风格
### 🎯 专业风险控制
- **单币种仓位上限**:
- 山寨币 1.5倍账户净值
- BTC/ETH 10倍账户净值
- **可配置杠杆** (v2.0.3+):
- 在config.json中设置最大杠杆
- 默认所有币种5倍子账户安全
- 主账户可增加山寨币最高20倍BTC/ETH最高50倍
- 币安子账户限制5倍杠杆
- **保证金管理**: 总使用率90%AI自主决策使用率
- **风险回报比**: 强制1:2止损:止盈
- **防止仓位叠加**: 同币种同方向不允许重复开仓
### 📊 通用市场数据层(加密货币实现)
- **多时间框架分析**3分钟实时 + 4小时趋势数据
- **技术指标**EMA20/50、MACD、RSI(7/14)、ATR
- **持仓量追踪**:市场情绪、资金流向分析
- **流动性过滤**:自动过滤低流动性资产(<15M USD
- **跨交易所支持**BinanceHyperliquidAster DEX统一数据接口
### 🎨 风格UI
- **专业交易界面**: 视觉设计
- **暗色主题**: 经典配色金色#F0B90B + 深色背景
- **实时数据**: 5秒刷新账户持仓图表
- **收益率曲线**: 账户净值历史走势美元/百分比切换
- **性能对比图**: 多AI收益率实时对比
- **动画效果**: 流畅的hover过渡加载动画
### 🎯 统一风控系统
- **仓位限制**单资产限制山寨币1.5x净值BTC/ETH10x净值
- **可配置杠杆**根据资产类别和账户类型动态调整 1x 50x
- **保证金管理**总使用率90%AI 控制分配
- **风险回报强制执行**强制1:2 的止损止盈比
- **防叠加保护**防止同一资产/方向的重复仓位
### 📝 完整决策记录
- **思维链记录**: AI的完整推理过程CoT
- **历史表现**: 整体胜率平均盈利盈亏比
- **最近交易**: 最近5笔交易详情开仓价平仓价盈亏%
- **币种统计**: 各币种表现胜率平均盈亏
- **JSON日志**: 每次决策完整记录便于复盘分析
### ⚡ 低延迟执行引擎
- **多交易所 API 集成**Binance FuturesHyperliquid DEXAster DEX
- **自动精度处理**每个交易所智能订单大小和价格格式化
- **优先级执行**先平仓现有持仓再开新仓
- **滑点控制**执行前验证实时精度检查
### 🎨 专业监控界面
- **币安风格仪表板**专业暗色主题实时更新
- **净值曲线**历史账户价值追踪USD/百分比切换
- **性能图表**多智能体 ROI 对比实时更新
- **完整决策日志**每笔交易的完整思维链CoT推理
- **5秒数据刷新**实时账户持仓和盈亏更新
---
## 🔮 路线图 - 通用市场扩展
我们经过验证的加密货币基础设施正在扩展到
- **📈 股票市场**美股A股港股
- **📊 期货市场**商品期货指数期货
- **🎯 期权交易**股票期权加密期权
- **💱 外汇市场**主要货币对交叉盘
**相同架构。相同智能体框架。所有市场。**
---
@@ -1310,8 +1344,9 @@ MIT License - 详见 [LICENSE](LICENSE) 文件
## 📬 联系方式
- **Twitter/X**: [@Web3Tinkle](https://x.com/Web3Tinkle)
### 🐛 技术支持
- **GitHub Issues**: [提交Issue](https://github.com/tinkle-community/nofx/issues)
- **开发者社区**: [Telegram群组](https://t.me/nofx_dev_community)
---

View File

@@ -235,9 +235,10 @@ type ExchangeConfig struct {
type UpdateModelConfigRequest struct {
Models map[string]struct {
Enabled bool `json:"enabled"`
APIKey string `json:"api_key"`
CustomAPIURL string `json:"custom_api_url"`
Enabled bool `json:"enabled"`
APIKey string `json:"api_key"`
CustomAPIURL string `json:"custom_api_url"`
CustomModelName string `json:"custom_model_name"`
} `json:"models"`
}
@@ -612,16 +613,23 @@ func (s *Server) handleUpdateModelConfigs(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 更新每个模型的配置
for modelID, modelData := range req.Models {
err := s.database.UpdateAIModel(userID, modelID, modelData.Enabled, modelData.APIKey, modelData.CustomAPIURL)
err := s.database.UpdateAIModel(userID, modelID, modelData.Enabled, modelData.APIKey, modelData.CustomAPIURL, modelData.CustomModelName)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("更新模型 %s 失败: %v", modelID, err)})
return
}
}
// 重新加载该用户的所有交易员,使新配置立即生效
err := s.traderManager.LoadUserTraders(s.database, userID)
if err != nil {
log.Printf("⚠️ 重新加载用户交易员到内存失败: %v", err)
// 这里不返回错误,因为模型配置已经成功更新到数据库
}
log.Printf("✓ AI模型配置已更新: %+v", req.Models)
c.JSON(http.StatusOK, gin.H{"message": "模型配置已更新"})
}
@@ -649,7 +657,7 @@ func (s *Server) handleUpdateExchangeConfigs(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 更新每个交易所的配置
for exchangeID, exchangeData := range req.Exchanges {
err := s.database.UpdateExchange(userID, exchangeID, exchangeData.Enabled, exchangeData.APIKey, exchangeData.SecretKey, exchangeData.Testnet, exchangeData.HyperliquidWalletAddr, exchangeData.AsterUser, exchangeData.AsterSigner, exchangeData.AsterPrivateKey)
@@ -658,7 +666,14 @@ func (s *Server) handleUpdateExchangeConfigs(c *gin.Context) {
return
}
}
// 重新加载该用户的所有交易员,使新配置立即生效
err := s.traderManager.LoadUserTraders(s.database, userID)
if err != nil {
log.Printf("⚠️ 重新加载用户交易员到内存失败: %v", err)
// 这里不返回错误,因为交易所配置已经成功更新到数据库
}
log.Printf("✓ 交易所配置已更新: %+v", req.Exchanges)
c.JSON(http.StatusOK, gin.H{"message": "交易所配置已更新"})
}
@@ -725,12 +740,21 @@ func (s *Server) handleTraderList(c *gin.Context) {
}
}
// AIModelID 应该已经是 provider如 "deepseek"),直接使用
// 如果是旧数据格式(如 "admin_deepseek"),提取 provider 部分
aiModelID := trader.AIModelID
// 兼容旧数据:如果包含下划线,提取最后一部分作为 provider
if strings.Contains(aiModelID, "_") {
parts := strings.Split(aiModelID, "_")
aiModelID = parts[len(parts)-1]
}
result = append(result, map[string]interface{}{
"trader_id": trader.ID,
"trader_name": trader.Name,
"ai_model": trader.AIModelID,
"exchange_id": trader.ExchangeID,
"is_running": isRunning,
"trader_id": trader.ID,
"trader_name": trader.Name,
"ai_model": aiModelID,
"exchange_id": trader.ExchangeID,
"is_running": isRunning,
"initial_balance": trader.InitialBalance,
})
}
@@ -763,21 +787,30 @@ func (s *Server) handleGetTraderConfig(c *gin.Context) {
}
}
// AIModelID 应该已经是 provider如 "deepseek"),直接使用
// 如果是旧数据格式(如 "admin_deepseek"),提取 provider 部分
aiModelID := traderConfig.AIModelID
// 兼容旧数据:如果包含下划线,提取最后一部分作为 provider
if strings.Contains(aiModelID, "_") {
parts := strings.Split(aiModelID, "_")
aiModelID = parts[len(parts)-1]
}
result := map[string]interface{}{
"trader_id": traderConfig.ID,
"trader_name": traderConfig.Name,
"ai_model": traderConfig.AIModelID,
"exchange_id": traderConfig.ExchangeID,
"initial_balance": traderConfig.InitialBalance,
"btc_eth_leverage": traderConfig.BTCETHLeverage,
"altcoin_leverage": traderConfig.AltcoinLeverage,
"trading_symbols": traderConfig.TradingSymbols,
"custom_prompt": traderConfig.CustomPrompt,
"trader_id": traderConfig.ID,
"trader_name": traderConfig.Name,
"ai_model": aiModelID,
"exchange_id": traderConfig.ExchangeID,
"initial_balance": traderConfig.InitialBalance,
"btc_eth_leverage": traderConfig.BTCETHLeverage,
"altcoin_leverage": traderConfig.AltcoinLeverage,
"trading_symbols": traderConfig.TradingSymbols,
"custom_prompt": traderConfig.CustomPrompt,
"override_base_prompt": traderConfig.OverrideBasePrompt,
"is_cross_margin": traderConfig.IsCrossMargin,
"use_coin_pool": traderConfig.UseCoinPool,
"use_oi_top": traderConfig.UseOITop,
"is_running": isRunning,
"is_cross_margin": traderConfig.IsCrossMargin,
"use_coin_pool": traderConfig.UseCoinPool,
"use_oi_top": traderConfig.UseOITop,
"is_running": isRunning,
}
c.JSON(http.StatusOK, result)

View File

@@ -185,6 +185,7 @@ func (d *Database) createTables() error {
`ALTER TABLE traders ADD COLUMN use_coin_pool BOOLEAN DEFAULT 0`, // 是否使用COIN POOL信号源
`ALTER TABLE traders ADD COLUMN use_oi_top BOOLEAN DEFAULT 0`, // 是否使用OI TOP信号源
`ALTER TABLE ai_models ADD COLUMN custom_api_url TEXT DEFAULT ''`, // 自定义API地址
`ALTER TABLE ai_models ADD COLUMN custom_model_name TEXT DEFAULT ''`, // 自定义模型名称
}
for _, query := range alterQueries {
@@ -362,15 +363,16 @@ type User struct {
// AIModelConfig AI模型配置
type AIModelConfig struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Name string `json:"name"`
Provider string `json:"provider"`
Enabled bool `json:"enabled"`
APIKey string `json:"apiKey"`
CustomAPIURL string `json:"customApiUrl"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID string `json:"id"`
UserID string `json:"user_id"`
Name string `json:"name"`
Provider string `json:"provider"`
Enabled bool `json:"enabled"`
APIKey string `json:"apiKey"`
CustomAPIURL string `json:"customApiUrl"`
CustomModelName string `json:"customModelName"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// ExchangeConfig 交易所配置
@@ -530,7 +532,10 @@ func (d *Database) UpdateUserOTPVerified(userID string, verified bool) error {
// GetAIModels 获取用户的AI模型配置
func (d *Database) GetAIModels(userID string) ([]*AIModelConfig, error) {
rows, err := d.db.Query(`
SELECT id, user_id, name, provider, enabled, api_key, COALESCE(custom_api_url, '') as custom_api_url, created_at, updated_at
SELECT id, user_id, name, provider, enabled, api_key,
COALESCE(custom_api_url, '') as custom_api_url,
COALESCE(custom_model_name, '') as custom_model_name,
created_at, updated_at
FROM ai_models WHERE user_id = ? ORDER BY id
`, userID)
if err != nil {
@@ -543,8 +548,8 @@ func (d *Database) GetAIModels(userID string) ([]*AIModelConfig, error) {
for rows.Next() {
var model AIModelConfig
err := rows.Scan(
&model.ID, &model.UserID, &model.Name, &model.Provider,
&model.Enabled, &model.APIKey, &model.CustomAPIURL,
&model.ID, &model.UserID, &model.Name, &model.Provider,
&model.Enabled, &model.APIKey, &model.CustomAPIURL, &model.CustomModelName,
&model.CreatedAt, &model.UpdatedAt,
)
if err != nil {
@@ -557,52 +562,50 @@ func (d *Database) GetAIModels(userID string) ([]*AIModelConfig, error) {
}
// UpdateAIModel 更新AI模型配置如果不存在则创建用户特定配置
func (d *Database) UpdateAIModel(userID, id string, enabled bool, apiKey, customAPIURL string) error {
// 首先尝试更新现有的用户配置
result, err := d.db.Exec(`
UPDATE ai_models SET enabled = ?, api_key = ?, custom_api_url = ? WHERE id = ? AND user_id = ?
`, enabled, apiKey, customAPIURL, id, userID)
if err != nil {
return err
}
// 检查是否有行被更新
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
// 如果没有行被更新,说明用户没有这个模型的配置,需要创建
if rowsAffected == 0 {
// 获取模型的基本信息
var name, provider string
err = d.db.QueryRow(`
SELECT name, provider FROM ai_models WHERE provider = ? LIMIT 1
`, id).Scan(&name, &provider)
if err != nil {
// 如果找不到基本信息,使用默认值
if id == "deepseek" {
name = "DeepSeek AI"
provider = "deepseek"
} else if id == "qwen" {
name = "Qwen AI"
provider = "qwen"
} else {
name = id + " AI"
provider = id
}
}
// 创建用户特定的配置
userModelID := fmt.Sprintf("%s_%s", userID, id)
func (d *Database) UpdateAIModel(userID, id string, enabled bool, apiKey, customAPIURL, customModelName string) error {
// id 参数实际上是 provider如 "deepseek", "qwen"
provider := id
// 先查找用户是否已有这个 provider 的配置
var existingID string
err := d.db.QueryRow(`
SELECT id FROM ai_models WHERE user_id = ? AND provider = ? LIMIT 1
`, userID, provider).Scan(&existingID)
if err == nil {
// 找到了现有配置,更新它
_, err = d.db.Exec(`
INSERT INTO ai_models (id, user_id, name, provider, enabled, api_key, custom_api_url, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
`, userModelID, userID, name, provider, enabled, apiKey, customAPIURL)
UPDATE ai_models SET enabled = ?, api_key = ?, custom_api_url = ?, custom_model_name = ?, updated_at = datetime('now')
WHERE id = ? AND user_id = ?
`, enabled, apiKey, customAPIURL, customModelName, existingID, userID)
return err
}
return nil
// 没有找到现有配置,创建新的
// 获取模型的基本信息
var name string
err = d.db.QueryRow(`
SELECT name FROM ai_models WHERE provider = ? LIMIT 1
`, provider).Scan(&name)
if err != nil {
// 如果找不到基本信息,使用默认值
if provider == "deepseek" {
name = "DeepSeek AI"
} else if provider == "qwen" {
name = "Qwen AI"
} else {
name = provider + " AI"
}
}
// 创建用户特定的配置
userModelID := fmt.Sprintf("%s_%s", userID, provider)
_, err = d.db.Exec(`
INSERT INTO ai_models (id, user_id, name, provider, enabled, api_key, custom_api_url, custom_model_name, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
`, userModelID, userID, name, provider, enabled, apiKey, customAPIURL, customModelName)
return err
}
// GetExchanges 获取用户的交易所配置

View File

@@ -226,7 +226,7 @@ func buildSystemPromptWithCustom(accountEquity float64, btcEthLeverage, altcoinL
sb.WriteString("# 📌 个性化交易策略\n\n")
sb.WriteString(customPrompt)
sb.WriteString("\n\n")
sb.WriteString("**注意**: 以上个性化策略是对基础规则的补充,不能违背基础风险控制原则。\n")
sb.WriteString("注意: 以上个性化策略是对基础规则的补充,不能违背基础风险控制原则。\n")
return sb.String()
}
@@ -236,111 +236,116 @@ func buildSystemPrompt(accountEquity float64, btcEthLeverage, altcoinLeverage in
var sb strings.Builder
// === 核心使命 ===
sb.WriteString("你是专业的加密货币交易AI币安合约市场进行自主交易。\n\n")
sb.WriteString("# 🎯 核心目标\n\n")
sb.WriteString("**最大化夏普比率Sharpe Ratio**\n\n")
sb.WriteString("你是专业的加密货币交易AI在合约市场进行自主交易。\n\n")
sb.WriteString("# 核心目标\n\n")
sb.WriteString("最大化夏普比率Sharpe Ratio\n\n")
sb.WriteString("夏普比率 = 平均收益 / 收益波动率\n\n")
sb.WriteString("**这意味着**\n")
sb.WriteString("- 高质量交易(高胜率、大盈亏比)→ 提升夏普\n")
sb.WriteString("- 稳定收益、控制回撤 → 提升夏普\n")
sb.WriteString("- 耐心持仓、让利润奔跑 → 提升夏普\n")
sb.WriteString("- 频繁交易、小盈小亏 → 增加波动,严重降低夏普\n")
sb.WriteString("- 过度交易、手续费损耗 → 直接亏损\n")
sb.WriteString("- 过早平仓、频繁进出 → 错失大行情\n\n")
sb.WriteString("**关键认知**: 系统每3分钟扫描一次但不意味着每次都要交易\n")
sb.WriteString("这意味着:\n")
sb.WriteString("- 高质量交易(高胜率、大盈亏比)→ 提升夏普\n")
sb.WriteString("- 稳定收益、控制回撤 → 提升夏普\n")
sb.WriteString("- 耐心持仓、让利润奔跑 → 提升夏普\n")
sb.WriteString("- 频繁交易、小盈小亏 → 增加波动,严重降低夏普\n")
sb.WriteString("- 过度交易、手续费损耗 → 直接亏损\n")
sb.WriteString("- 过早平仓、频繁进出 → 错失大行情\n\n")
sb.WriteString("关键认知: 系统每3分钟扫描一次但不意味着每次都要交易\n")
sb.WriteString("大多数时候应该是 `wait` 或 `hold`,只在极佳机会时才开仓。\n\n")
// === 硬约束(风险控制)===
sb.WriteString("# ⚖️ 硬约束(风险控制)\n\n")
sb.WriteString("1. **风险回报比**: 必须 ≥ 1:3冒1%风险赚3%+收益)\n")
sb.WriteString("2. **最多持仓**: 3个币种质量>数量)\n")
sb.WriteString(fmt.Sprintf("3. **单币仓位**: 山寨%.0f-%.0f U(%dx杠杆) | BTC/ETH %.0f-%.0f U(%dx杠杆)\n",
sb.WriteString("# 硬约束(风险控制)\n\n")
sb.WriteString("1. 风险回报比: 必须 ≥ 1:3冒1%风险赚3%+收益)\n")
sb.WriteString("2. 最多持仓: 3个币种质量>数量)\n")
sb.WriteString(fmt.Sprintf("3. 单币仓位: 山寨%.0f-%.0f U(%dx杠杆) | BTC/ETH %.0f-%.0f U(%dx杠杆)\n",
accountEquity*0.8, accountEquity*1.5, altcoinLeverage, accountEquity*5, accountEquity*10, btcEthLeverage))
sb.WriteString("4. **保证金**: 总使用率 ≤ 90%\n\n")
sb.WriteString("4. 保证金: 总使用率 ≤ 90%\n\n")
// === 做空激励 ===
sb.WriteString("# 📉 做多做空平衡\n\n")
sb.WriteString("**重要**: 下跌趋势做空的利润 = 上涨趋势做多的利润\n\n")
sb.WriteString("- 上涨趋势 → 做多\n")
sb.WriteString("- 下跌趋势 → 做空\n")
sb.WriteString("- 震荡市场 → 观望\n\n")
sb.WriteString("**不要有做多偏见!做空是你的核心工具之一**\n\n")
// === 交易哲学 & 最佳实践 ===
sb.WriteString("# 交易哲学 & 最佳实践\n\n")
sb.WriteString("## 核心原则:\n\n")
sb.WriteString("资金保全第一:保护资本比追求收益更重要\n\n")
sb.WriteString("纪律胜于情绪:执行你的退出方案,不随意移动止损或目标\n\n")
sb.WriteString("质量优于数量:少量高信念交易胜过大量低信念交易\n\n")
sb.WriteString("适应波动性:根据市场条件调整仓位\n\n")
sb.WriteString("尊重趋势:不要与强趋势作对\n\n")
sb.WriteString("## 常见误区避免:\n\n")
sb.WriteString("过度交易:频繁交易导致费用侵蚀利润\n\n")
sb.WriteString("复仇式交易:亏损后立即加码试图\"翻本\"\n\n")
sb.WriteString("分析瘫痪:过度等待完美信号,导致失机\n\n")
sb.WriteString("忽视相关性BTC常引领山寨币须优先观察BTC\n\n")
sb.WriteString("过度杠杆:放大收益同时放大亏损\n\n")
// === 交易频率认知 ===
sb.WriteString("# ⏱️ 交易频率认知\n\n")
sb.WriteString("**量化标准**:\n")
sb.WriteString("#交易频率认知\n\n")
sb.WriteString("量化标准:\n")
sb.WriteString("- 优秀交易员每天2-4笔 = 每小时0.1-0.2笔\n")
sb.WriteString("- 过度交易:每小时>2笔 = 严重问题\n")
sb.WriteString("- 最佳节奏开仓后持有至少30-60分钟\n\n")
sb.WriteString("**自查**:\n")
sb.WriteString("自查:\n")
sb.WriteString("如果你发现自己每个周期都在交易 → 说明标准太低\n")
sb.WriteString("如果你发现持仓<30分钟就平仓 → 说明太急躁\n\n")
// === 开仓信号强度 ===
sb.WriteString("# 🎯 开仓标准(严格)\n\n")
sb.WriteString("只在**强信号**时开仓,不确定就观望。\n\n")
sb.WriteString("**你拥有的完整数据**\n")
sb.WriteString("- 📊 **原始序列**3分钟价格序列(MidPrices数组) + 4小时K线序列\n")
sb.WriteString("- 📈 **技术序列**EMA20序列、MACD序列、RSI7序列、RSI14序列\n")
sb.WriteString("- 💰 **资金序列**:成交量序列、持仓量(OI)序列、资金费率\n")
sb.WriteString("- 🎯 **筛选标记**AI500评分 / OI_Top排名如果有标注\n\n")
sb.WriteString("**分析方法**(完全由你自主决定):\n")
sb.WriteString("# 开仓标准(严格)\n\n")
sb.WriteString("只在强信号时开仓,不确定就观望。\n\n")
sb.WriteString("你拥有的完整数据:\n")
sb.WriteString("- 原始序列3分钟价格序列(MidPrices数组) + 4小时K线序列\n")
sb.WriteString("- 技术序列EMA20序列、MACD序列、RSI7序列、RSI14序列\n")
sb.WriteString("- 资金序列:成交量序列、持仓量(OI)序列、资金费率\n")
sb.WriteString("- 筛选标记AI500评分 / OI_Top排名如果有标注\n\n")
sb.WriteString("分析方法(完全由你自主决定):\n")
sb.WriteString("- 自由运用序列数据,你可以做但不限于趋势分析、形态识别、支撑阻力、技术阻力位、斐波那契、波动带计算\n")
sb.WriteString("- 多维度交叉验证(价格+量+OI+指标+序列形态)\n")
sb.WriteString("- 用你认为最有效的方法发现高确定性机会\n")
sb.WriteString("- 综合信心度 ≥ 75 才开仓\n\n")
sb.WriteString("**避免低质量信号**\n")
sb.WriteString("避免低质量信号:\n")
sb.WriteString("- 单一维度(只看一个指标)\n")
sb.WriteString("- 相互矛盾(涨但量萎缩)\n")
sb.WriteString("- 横盘震荡\n")
sb.WriteString("- 刚平仓不久(<15分钟\n\n")
// === 夏普比率自我进化 ===
sb.WriteString("# 🧬 夏普比率自我进化\n\n")
sb.WriteString("每次你会收到**夏普比率**作为绩效反馈(周期级别):\n\n")
sb.WriteString("**夏普比率 < -0.5** (持续亏损):\n")
sb.WriteString(" → 🛑 停止交易连续观望至少6个周期18分钟\n")
sb.WriteString(" → 🔍 深度反思:\n")
sb.WriteString("# 夏普比率自我进化\n\n")
sb.WriteString("每次你会收到夏普比率作为绩效反馈(周期级别):\n\n")
sb.WriteString("夏普比率 < -0.5 (持续亏损):\n")
sb.WriteString(" → 停止交易连续观望至少6个周期18分钟\n")
sb.WriteString(" → 深度反思:\n")
sb.WriteString(" • 交易频率过高?(每小时>2次就是过度\n")
sb.WriteString(" • 持仓时间过短?(<30分钟就是过早平仓\n")
sb.WriteString(" • 信号强度不足?(信心度<75\n")
sb.WriteString(" • 是否在做空?(单边做多是错误的)\n\n")
sb.WriteString("**夏普比率 -0.5 ~ 0** (轻微亏损):\n")
sb.WriteString(" → ⚠️ 严格控制:只做信心度>80的交易\n")
sb.WriteString("夏普比率 -0.5 ~ 0 (轻微亏损):\n")
sb.WriteString(" → 严格控制:只做信心度>80的交易\n")
sb.WriteString(" → 减少交易频率每小时最多1笔新开仓\n")
sb.WriteString(" → 耐心持仓至少持有30分钟以上\n\n")
sb.WriteString("**夏普比率 0 ~ 0.7** (正收益):\n")
sb.WriteString(" → 维持当前策略\n\n")
sb.WriteString("**夏普比率 > 0.7** (优异表现):\n")
sb.WriteString(" → 🚀 可适度扩大仓位\n\n")
sb.WriteString("**关键**: 夏普比率是唯一指标,它会自然惩罚频繁交易和过度进出。\n\n")
sb.WriteString("夏普比率 0 ~ 0.7 (正收益):\n")
sb.WriteString(" → 维持当前策略\n\n")
sb.WriteString("夏普比率 > 0.7 (优异表现):\n")
sb.WriteString(" → 可适度扩大仓位\n\n")
sb.WriteString("关键: 夏普比率是唯一指标,它会自然惩罚频繁交易和过度进出。\n\n")
// === 决策流程 ===
sb.WriteString("# 📋 决策流程\n\n")
sb.WriteString("1. **分析夏普比率**: 当前策略是否有效?需要调整吗?\n")
sb.WriteString("2. **评估持仓**: 趋势是否改变?是否该止盈/止损?\n")
sb.WriteString("3. **寻找新机会**: 有强信号吗?多空机会?\n")
sb.WriteString("4. **输出决策**: 思维链分析 + JSON\n\n")
sb.WriteString("#决策流程\n\n")
sb.WriteString("1. 分析夏普比率: 当前策略是否有效?需要调整吗?\n")
sb.WriteString("2. 评估持仓: 趋势是否改变?是否该止盈/止损?\n")
sb.WriteString("3. 寻找新机会: 有强信号吗?多空机会?\n")
sb.WriteString("4. 输出决策: 思维链分析 + JSON\n\n")
// === 输出格式 ===
sb.WriteString("# 📤 输出格式\n\n")
sb.WriteString("**第一步: 思维链(纯文本)**\n")
sb.WriteString("#输出格式\n\n")
sb.WriteString("第一步: 思维链(纯文本)\n")
sb.WriteString("简洁分析你的思考过程\n\n")
sb.WriteString("**第二步: JSON决策数组**\n\n")
sb.WriteString("第二步: JSON决策数组\n\n")
sb.WriteString("```json\n[\n")
sb.WriteString(fmt.Sprintf(" {\"symbol\": \"BTCUSDT\", \"action\": \"open_short\", \"leverage\": %d, \"position_size_usd\": %.0f, \"stop_loss\": 97000, \"take_profit\": 91000, \"confidence\": 85, \"risk_usd\": 300, \"reasoning\": \"下跌趋势+MACD死叉\"},\n", btcEthLeverage, accountEquity*5))
sb.WriteString(" {\"symbol\": \"ETHUSDT\", \"action\": \"close_long\", \"reasoning\": \"止盈离场\"}\n")
sb.WriteString("]\n```\n\n")
sb.WriteString("**字段说明**:\n")
sb.WriteString("字段说明:\n")
sb.WriteString("- `action`: open_long | open_short | close_long | close_short | hold | wait\n")
sb.WriteString("- `confidence`: 0-100开仓建议≥75\n")
sb.WriteString("- 开仓时必填: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd, reasoning\n\n")
// === 关键提醒 ===
sb.WriteString("---\n\n")
sb.WriteString("**记住**: \n")
sb.WriteString("记住: \n")
sb.WriteString("- 目标是夏普比率,不是交易频率\n")
sb.WriteString("- 做空 = 做多,都是赚钱工具\n")
sb.WriteString("- 宁可错过,不做低质量交易\n")
sb.WriteString("- 风险回报比1:3是底线\n")
@@ -352,18 +357,18 @@ func buildUserPrompt(ctx *Context) string {
var sb strings.Builder
// 系统状态
sb.WriteString(fmt.Sprintf("**时间**: %s | **周期**: #%d | **运行**: %d分钟\n\n",
sb.WriteString(fmt.Sprintf("时间: %s | 周期: #%d | 运行: %d分钟\n\n",
ctx.CurrentTime, ctx.CallCount, ctx.RuntimeMinutes))
// BTC 市场
if btcData, hasBTC := ctx.MarketDataMap["BTCUSDT"]; hasBTC {
sb.WriteString(fmt.Sprintf("**BTC**: %.2f (1h: %+.2f%%, 4h: %+.2f%%) | MACD: %.4f | RSI: %.2f\n\n",
sb.WriteString(fmt.Sprintf("BTC: %.2f (1h: %+.2f%%, 4h: %+.2f%%) | MACD: %.4f | RSI: %.2f\n\n",
btcData.CurrentPrice, btcData.PriceChange1h, btcData.PriceChange4h,
btcData.CurrentMACD, btcData.CurrentRSI7))
}
// 账户
sb.WriteString(fmt.Sprintf("**账户**: 净值%.2f | 余额%.2f (%.1f%%) | 盈亏%+.2f%% | 保证金%.1f%% | 持仓%d个\n\n",
sb.WriteString(fmt.Sprintf("账户: 净值%.2f | 余额%.2f (%.1f%%) | 盈亏%+.2f%% | 保证金%.1f%% | 持仓%d个\n\n",
ctx.Account.TotalEquity,
ctx.Account.AvailableBalance,
(ctx.Account.AvailableBalance/ctx.Account.TotalEquity)*100,
@@ -401,7 +406,7 @@ func buildUserPrompt(ctx *Context) string {
}
}
} else {
sb.WriteString("**当前持仓**: 无\n\n")
sb.WriteString("当前持仓: 无\n\n")
}
// 候选币种(完整市场数据)

View File

@@ -94,7 +94,9 @@ func (tm *TraderManager) LoadTradersFromDatabase(database *config.Database) erro
var aiModelCfg *config.AIModelConfig
for _, model := range aiModels {
if model.ID == traderCfg.AIModelID {
// 使用 provider 来匹配,因为 AIModelID 存储的是 provider如 "deepseek"
// 而 model.ID 可能是 "admin_deepseek"
if model.Provider == traderCfg.AIModelID {
aiModelCfg = model
break
}
@@ -202,6 +204,8 @@ func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModel
UseQwen: aiModelCfg.Provider == "qwen",
DeepSeekKey: "",
QwenKey: "",
CustomAPIURL: aiModelCfg.CustomAPIURL, // 自定义API URL
CustomModelName: aiModelCfg.CustomModelName, // 自定义模型名称
ScanInterval: time.Duration(traderCfg.ScanIntervalMinutes) * time.Minute,
InitialBalance: traderCfg.InitialBalance,
BTCETHLeverage: traderCfg.BTCETHLeverage,
@@ -306,6 +310,8 @@ func (tm *TraderManager) AddTraderFromDB(traderCfg *config.TraderRecord, aiModel
UseQwen: aiModelCfg.Provider == "qwen",
DeepSeekKey: "",
QwenKey: "",
CustomAPIURL: aiModelCfg.CustomAPIURL, // 自定义API URL
CustomModelName: aiModelCfg.CustomModelName, // 自定义模型名称
ScanInterval: time.Duration(traderCfg.ScanIntervalMinutes) * time.Minute,
InitialBalance: traderCfg.InitialBalance,
BTCETHLeverage: traderCfg.BTCETHLeverage,
@@ -616,7 +622,8 @@ func (tm *TraderManager) LoadUserTraders(database *config.Database, userID strin
var aiModelCfg *config.AIModelConfig
for _, model := range aiModels {
if model.ID == traderCfg.AIModelID {
// 使用 provider 来匹配,因为 AIModelID 存储的是 provider如 "deepseek"
if model.Provider == traderCfg.AIModelID {
aiModelCfg = model
break
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
@@ -23,7 +24,6 @@ const (
type Client struct {
Provider Provider
APIKey string
SecretKey string // 阿里云需要
BaseURL string
Model string
Timeout time.Duration
@@ -41,20 +41,53 @@ func New() *Client {
}
// SetDeepSeekAPIKey 设置DeepSeek API密钥
func (client *Client) SetDeepSeekAPIKey(apiKey string) {
// customURL 为空时使用默认URLcustomModel 为空时使用默认模型
func (client *Client) SetDeepSeekAPIKey(apiKey string, customURL string, customModel string) {
client.Provider = ProviderDeepSeek
client.APIKey = apiKey
client.BaseURL = "https://api.deepseek.com/v1"
client.Model = "deepseek-chat"
if customURL != "" {
client.BaseURL = customURL
log.Printf("🔧 [MCP] DeepSeek 使用自定义 BaseURL: %s", customURL)
} else {
client.BaseURL = "https://api.deepseek.com/v1"
log.Printf("🔧 [MCP] DeepSeek 使用默认 BaseURL: %s", client.BaseURL)
}
if customModel != "" {
client.Model = customModel
log.Printf("🔧 [MCP] DeepSeek 使用自定义 Model: %s", customModel)
} else {
client.Model = "deepseek-chat"
log.Printf("🔧 [MCP] DeepSeek 使用默认 Model: %s", client.Model)
}
// 打印 API Key 的前后各4位用于验证
if len(apiKey) > 8 {
log.Printf("🔧 [MCP] DeepSeek API Key: %s...%s", apiKey[:4], apiKey[len(apiKey)-4:])
}
}
// SetQwenAPIKey 设置阿里云Qwen API密钥
func (client *Client) SetQwenAPIKey(apiKey, secretKey string) {
// customURL 为空时使用默认URLcustomModel 为空时使用默认模型
func (client *Client) SetQwenAPIKey(apiKey string, customURL string, customModel string) {
client.Provider = ProviderQwen
client.APIKey = apiKey
client.SecretKey = secretKey
client.BaseURL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
client.Model = "qwen-plus" // 可选: qwen-turbo, qwen-plus, qwen-max
if customURL != "" {
client.BaseURL = customURL
log.Printf("🔧 [MCP] Qwen 使用自定义 BaseURL: %s", customURL)
} else {
client.BaseURL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
log.Printf("🔧 [MCP] Qwen 使用默认 BaseURL: %s", client.BaseURL)
}
if customModel != "" {
client.Model = customModel
log.Printf("🔧 [MCP] Qwen 使用自定义 Model: %s", customModel)
} else {
client.Model = "qwen-plus" // 可选: qwen-turbo, qwen-plus, qwen-max
log.Printf("🔧 [MCP] Qwen 使用默认 Model: %s", client.Model)
}
// 打印 API Key 的前后各4位用于验证
if len(apiKey) > 8 {
log.Printf("🔧 [MCP] Qwen API Key: %s...%s", apiKey[:4], apiKey[len(apiKey)-4:])
}
}
// SetCustomAPI 设置自定义OpenAI兼容API
@@ -125,6 +158,16 @@ func (client *Client) CallWithMessages(systemPrompt, userPrompt string) (string,
// callOnce 单次调用AI API内部使用
func (client *Client) callOnce(systemPrompt, userPrompt string) (string, error) {
// 打印当前 AI 配置
log.Printf("📡 [MCP] AI 请求配置:")
log.Printf(" Provider: %s", client.Provider)
log.Printf(" BaseURL: %s", client.BaseURL)
log.Printf(" Model: %s", client.Model)
log.Printf(" UseFullURL: %v", client.UseFullURL)
if len(client.APIKey) > 8 {
log.Printf(" API Key: %s...%s", client.APIKey[:4], client.APIKey[len(client.APIKey)-4:])
}
// 构建 messages 数组
messages := []map[string]string{}
@@ -167,6 +210,8 @@ func (client *Client) callOnce(systemPrompt, userPrompt string) (string, error)
// 默认行为:添加/chat/completions
url = fmt.Sprintf("%s/chat/completions", client.BaseURL)
}
log.Printf("📡 [MCP] 请求 URL: %s", url)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("创建请求失败: %w", err)

View File

@@ -121,13 +121,21 @@ func NewAutoTrader(config AutoTraderConfig) (*AutoTrader, error) {
mcpClient.SetCustomAPI(config.CustomAPIURL, config.CustomAPIKey, config.CustomModelName)
log.Printf("🤖 [%s] 使用自定义AI API: %s (模型: %s)", config.Name, config.CustomAPIURL, config.CustomModelName)
} else if config.UseQwen || config.AIModel == "qwen" {
// 使用Qwen
mcpClient.SetQwenAPIKey(config.QwenKey, "")
log.Printf("🤖 [%s] 使用阿里云Qwen AI", config.Name)
// 使用Qwen (支持自定义URL和Model)
mcpClient.SetQwenAPIKey(config.QwenKey, config.CustomAPIURL, config.CustomModelName)
if config.CustomAPIURL != "" || config.CustomModelName != "" {
log.Printf("🤖 [%s] 使用阿里云Qwen AI (自定义URL: %s, 模型: %s)", config.Name, config.CustomAPIURL, config.CustomModelName)
} else {
log.Printf("🤖 [%s] 使用阿里云Qwen AI", config.Name)
}
} else {
// 默认使用DeepSeek
mcpClient.SetDeepSeekAPIKey(config.DeepSeekKey)
log.Printf("🤖 [%s] 使用DeepSeek AI", config.Name)
// 默认使用DeepSeek (支持自定义URL和Model)
mcpClient.SetDeepSeekAPIKey(config.DeepSeekKey, config.CustomAPIURL, config.CustomModelName)
if config.CustomAPIURL != "" || config.CustomModelName != "" {
log.Printf("🤖 [%s] 使用DeepSeek AI (自定义URL: %s, 模型: %s)", config.Name, config.CustomAPIURL, config.CustomModelName)
} else {
log.Printf("🤖 [%s] 使用DeepSeek AI", config.Name)
}
}
// 初始化币种池API

View File

@@ -179,7 +179,7 @@ function App() {
style={{ background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)' }}>
</div>
<p style={{ color: '#EAECEF' }}>...</p>
<p style={{ color: '#EAECEF' }}>{t('loading', language)}</p>
</div>
</div>
);
@@ -299,7 +299,7 @@ function App() {
className="px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D', border: '1px solid rgba(246, 70, 93, 0.2)' }}
>
退
{t('logout', language)}
</button>
)}
</div>

View File

@@ -3,7 +3,7 @@ import useSWR from 'swr';
import { api } from '../lib/api';
import type { TraderInfo, CreateTraderRequest, AIModel, Exchange } from '../types';
import { useLanguage } from '../contexts/LanguageContext';
import { t, Language } from '../i18n/translations';
import { t, type Language } from '../i18n/translations';
import { getExchangeIcon } from './ExchangeIcons';
import { getModelIcon } from './ModelIcons';
import { TraderConfigModal } from './TraderConfigModal';
@@ -134,25 +134,25 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
const handleCreateTrader = async (data: CreateTraderRequest) => {
try {
const model = allModels?.find(m => m.id === data.ai_model_id);
const model = allModels?.find(m => m.provider === data.ai_model_id);
const exchange = allExchanges?.find(e => e.id === data.exchange_id);
if (!model?.enabled) {
alert(t('modelNotConfigured', language));
return;
}
if (!exchange?.enabled) {
alert(t('exchangeNotConfigured', language));
return;
}
await api.createTrader(data);
setShowCreateModal(false);
mutateTraders();
} catch (error) {
console.error('Failed to create trader:', error);
alert('创建交易员失败');
alert(t('createTraderFailed', language));
}
};
@@ -163,24 +163,24 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setShowEditModal(true);
} catch (error) {
console.error('Failed to fetch trader config:', error);
alert('获取交易员配置失败');
alert(t('getTraderConfigFailed', language));
}
};
const handleSaveEditTrader = async (data: CreateTraderRequest) => {
if (!editingTrader) return;
try {
const model = enabledModels?.find(m => m.id === data.ai_model_id);
const model = enabledModels?.find(m => m.provider === data.ai_model_id);
const exchange = enabledExchanges?.find(e => e.id === data.exchange_id);
if (!model) {
alert('AI模型配置不存在或未启用');
alert(t('modelConfigNotExist', language));
return;
}
if (!exchange) {
alert('交易所配置不存在或未启用');
alert(t('exchangeConfigNotExist', language));
return;
}
@@ -205,19 +205,19 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
mutateTraders();
} catch (error) {
console.error('Failed to update trader:', error);
alert('更新交易员失败');
alert(t('updateTraderFailed', language));
}
};
const handleDeleteTrader = async (traderId: string) => {
if (!confirm(t('confirmDeleteTrader', language))) return;
try {
await api.deleteTrader(traderId);
mutateTraders();
} catch (error) {
console.error('Failed to delete trader:', error);
alert('删除交易员失败');
alert(t('deleteTraderFailed', language));
}
};
@@ -231,7 +231,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
mutateTraders();
} catch (error) {
console.error('Failed to toggle trader:', error);
alert('操作失败');
alert(t('operationFailed', language));
}
};
@@ -250,88 +250,91 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
};
const handleDeleteModelConfig = async (modelId: string) => {
if (!confirm('确定要删除此AI模型配置吗')) return;
if (!confirm(t('confirmDeleteModel', language))) return;
try {
const updatedModels = allModels?.map(m =>
m.id === modelId ? { ...m, apiKey: '', enabled: false } : m
const updatedModels = allModels?.map(m =>
m.id === modelId ? { ...m, apiKey: '', customApiUrl: '', customModelName: '', enabled: false } : m
) || [];
const request = {
models: Object.fromEntries(
updatedModels.map(model => [
model.id,
model.provider, // 使用 provider 而不是 id
{
enabled: model.enabled,
api_key: model.apiKey || ''
api_key: model.apiKey || '',
custom_api_url: model.customApiUrl || '',
custom_model_name: model.customModelName || ''
}
])
)
};
await api.updateModelConfigs(request);
setAllModels(updatedModels);
setShowModelModal(false);
setEditingModel(null);
} catch (error) {
console.error('Failed to delete model config:', error);
alert('删除配置失败');
alert(t('deleteConfigFailed', language));
}
};
const handleSaveModelConfig = async (modelId: string, apiKey: string, customApiUrl?: string) => {
const handleSaveModelConfig = async (modelId: string, apiKey: string, customApiUrl?: string, customModelName?: string) => {
try {
// 找到要配置的模型从supportedModels中
const modelToUpdate = supportedModels?.find(m => m.id === modelId);
if (!modelToUpdate) {
alert('模型不存在');
alert(t('modelNotExist', language));
return;
}
// 创建或更新用户的模型配置
const existingModel = allModels?.find(m => m.id === modelId);
let updatedModels;
if (existingModel) {
// 更新现有配置
updatedModels = allModels?.map(m =>
m.id === modelId ? { ...m, apiKey, customApiUrl: customApiUrl || '', enabled: true } : m
updatedModels = allModels?.map(m =>
m.id === modelId ? { ...m, apiKey, customApiUrl: customApiUrl || '', customModelName: customModelName || '', enabled: true } : m
) || [];
} else {
// 添加新配置
const newModel = { ...modelToUpdate, apiKey, customApiUrl: customApiUrl || '', enabled: true };
const newModel = { ...modelToUpdate, apiKey, customApiUrl: customApiUrl || '', customModelName: customModelName || '', enabled: true };
updatedModels = [...(allModels || []), newModel];
}
const request = {
models: Object.fromEntries(
updatedModels.map(model => [
model.id,
model.provider, // 使用 provider 而不是 id
{
enabled: model.enabled,
api_key: model.apiKey || '',
custom_api_url: model.customApiUrl || ''
custom_api_url: model.customApiUrl || '',
custom_model_name: model.customModelName || ''
}
])
)
};
await api.updateModelConfigs(request);
// 重新获取用户配置以确保数据同步
const refreshedModels = await api.getModelConfigs();
setAllModels(refreshedModels);
setShowModelModal(false);
setEditingModel(null);
} catch (error) {
console.error('Failed to save model config:', error);
alert('保存配置失败');
alert(t('saveConfigFailed', language));
}
};
const handleDeleteExchangeConfig = async (exchangeId: string) => {
if (!confirm('确定要删除此交易所配置吗?')) return;
if (!confirm(t('confirmDeleteExchange', language))) return;
try {
const updatedExchanges = allExchanges?.map(e =>
@@ -358,7 +361,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setEditingExchange(null);
} catch (error) {
console.error('Failed to delete exchange config:', error);
alert('删除交易所配置失败');
alert(t('deleteExchangeConfigFailed', language));
}
};
@@ -367,7 +370,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
// 找到要配置的交易所从supportedExchanges中
const exchangeToUpdate = supportedExchanges?.find(e => e.id === exchangeId);
if (!exchangeToUpdate) {
alert('交易所不存在');
alert(t('exchangeNotExist', language));
return;
}
@@ -434,7 +437,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setEditingExchange(null);
} catch (error) {
console.error('Failed to save exchange config:', error);
alert('保存交易所配置失败');
alert(t('saveConfigFailed', language));
}
};
@@ -455,7 +458,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setShowSignalSourceModal(false);
} catch (error) {
console.error('Failed to save signal source:', error);
alert('保存信号源配置失败');
alert(t('saveSignalSourceFailed', language));
}
};
@@ -516,13 +519,13 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
<button
onClick={() => setShowSignalSourceModal(true)}
className="px-4 py-2 rounded text-sm font-semibold transition-all hover:scale-105"
style={{
background: '#2B3139',
color: '#EAECEF',
border: '1px solid #474D57'
style={{
background: '#2B3139',
color: '#EAECEF',
border: '1px solid #474D57'
}}
>
📡
📡 {t('signalSource', language)}
</button>
<button
@@ -575,7 +578,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
<div>
<div className="font-semibold" style={{ color: '#EAECEF' }}>{getShortName(model.name)}</div>
<div className="text-xs" style={{ color: '#848E9C' }}>
{inUse ? '正在使用' : model.enabled ? '已启用' : '已配置'}
{inUse ? t('inUse', language) : model.enabled ? t('enabled', language) : t('configured', language)}
</div>
</div>
</div>
@@ -586,7 +589,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{configuredModels.length === 0 && (
<div className="text-center py-8" style={{ color: '#848E9C' }}>
<Brain className="w-12 h-12 mx-auto mb-2 opacity-50" />
<div className="text-sm">AI模型</div>
<div className="text-sm">{t('noModelsConfigured', language)}</div>
</div>
)}
</div>
@@ -617,7 +620,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
<div>
<div className="font-semibold" style={{ color: '#EAECEF' }}>{getShortName(exchange.name)}</div>
<div className="text-xs" style={{ color: '#848E9C' }}>
{exchange.type.toUpperCase()} {inUse ? '正在使用' : exchange.enabled ? '已启用' : '已配置'}
{exchange.type.toUpperCase()} {inUse ? t('inUse', language) : exchange.enabled ? t('enabled', language) : t('configured', language)}
</div>
</div>
</div>
@@ -628,7 +631,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
{configuredExchanges.length === 0 && (
<div className="text-center py-8" style={{ color: '#848E9C' }}>
<Landmark className="w-12 h-12 mx-auto mb-2 opacity-50" />
<div className="text-sm"></div>
<div className="text-sm">{t('noExchangesConfigured', language)}</div>
</div>
)}
</div>
@@ -692,19 +695,19 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
style={{ background: 'rgba(99, 102, 241, 0.1)', color: '#6366F1' }}
>
<BarChart3 className="w-4 h-4" />
{t('view', language)}
</button>
<button
onClick={() => handleEditTrader(trader.trader_id)}
disabled={trader.is_running}
className="px-3 py-2 rounded text-sm font-semibold transition-all hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed"
style={{
background: trader.is_running ? 'rgba(132, 142, 156, 0.1)' : 'rgba(255, 193, 7, 0.1)',
color: trader.is_running ? '#848E9C' : '#FFC107'
style={{
background: trader.is_running ? 'rgba(132, 142, 156, 0.1)' : 'rgba(255, 193, 7, 0.1)',
color: trader.is_running ? '#848E9C' : '#FFC107'
}}
>
{t('edit', language)}
</button>
<button
@@ -789,6 +792,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
setShowModelModal(false);
setEditingModel(null);
}}
language={language}
/>
)}
@@ -814,6 +818,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
oiTopUrl={userSignalSource.oiTopUrl}
onSave={handleSaveSignalSource}
onClose={() => setShowSignalSourceModal(false)}
language={language}
/>
)}
</div>
@@ -825,12 +830,14 @@ function SignalSourceModal({
coinPoolUrl,
oiTopUrl,
onSave,
onClose
onClose,
language
}: {
coinPoolUrl: string;
oiTopUrl: string;
onSave: (coinPoolUrl: string, oiTopUrl: string) => void;
onClose: () => void;
language: Language;
}) {
const [coinPool, setCoinPool] = useState(coinPoolUrl || '');
const [oiTop, setOiTop] = useState(oiTopUrl || '');
@@ -844,7 +851,7 @@ function SignalSourceModal({
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-gray-800 rounded-lg p-6 w-full max-w-lg relative" style={{ background: '#1E2329' }}>
<h3 className="text-xl font-bold mb-4" style={{ color: '#EAECEF' }}>
📡
📡 {t('signalSourceConfig', language)}
</h3>
<form onSubmit={handleSubmit} className="space-y-4">
@@ -861,7 +868,7 @@ function SignalSourceModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
API地址使
{t('coinPoolDescription', language)}
</div>
</div>
@@ -878,18 +885,18 @@ function SignalSourceModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
API地址使
{t('oiTopDescription', language)}
</div>
</div>
<div className="p-4 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
<div className="text-sm font-semibold mb-2" style={{ color: '#F0B90B' }}>
{t('information', language)}
</div>
<div className="text-xs space-y-1" style={{ color: '#848E9C' }}>
<div> URL</div>
<div> 使</div>
<div> URL将用于获取市场数据和交易信号</div>
<div>{t('signalSourceInfo1', language)}</div>
<div>{t('signalSourceInfo2', language)}</div>
<div>{t('signalSourceInfo3', language)}</div>
</div>
</div>
@@ -900,14 +907,14 @@ function SignalSourceModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold"
style={{ background: '#2B3139', color: '#848E9C' }}
>
{t('cancel', language)}
</button>
<button
type="submit"
className="flex-1 px-4 py-2 rounded text-sm font-semibold"
style={{ background: '#F0B90B', color: '#000' }}
>
{t('save', language)}
</button>
</div>
</form>
@@ -923,37 +930,41 @@ function ModelConfigModal({
editingModelId,
onSave,
onDelete,
onClose
onClose,
language
}: {
allModels: AIModel[];
configuredModels: AIModel[];
editingModelId: string | null;
onSave: (modelId: string, apiKey: string, baseUrl?: string) => void;
onSave: (modelId: string, apiKey: string, baseUrl?: string, modelName?: string) => void;
onDelete: (modelId: string) => void;
onClose: () => void;
language: Language;
}) {
const [selectedModelId, setSelectedModelId] = useState(editingModelId || '');
const [apiKey, setApiKey] = useState('');
const [baseUrl, setBaseUrl] = useState('');
const [modelName, setModelName] = useState('');
// 获取当前编辑的模型信息 - 编辑时从已配置的模型中查找,新建时从所有支持的模型中查找
const selectedModel = editingModelId
? configuredModels?.find(m => m.id === selectedModelId)
const selectedModel = editingModelId
? configuredModels?.find(m => m.id === selectedModelId)
: allModels?.find(m => m.id === selectedModelId);
// 如果是编辑现有模型初始化API KeyBase URL
// 如果是编辑现有模型初始化API KeyBase URL和Model Name
useEffect(() => {
if (editingModelId && selectedModel) {
setApiKey(selectedModel.apiKey || '');
setBaseUrl(selectedModel.customApiUrl || '');
setModelName(selectedModel.customModelName || '');
}
}, [editingModelId, selectedModel]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!selectedModelId || !apiKey.trim()) return;
onSave(selectedModelId, apiKey.trim(), baseUrl.trim() || undefined);
onSave(selectedModelId, apiKey.trim(), baseUrl.trim() || undefined, modelName.trim() || undefined);
};
// 可选择的模型列表(所有支持的模型)
@@ -964,30 +975,30 @@ function ModelConfigModal({
<div className="bg-gray-800 rounded-lg p-6 w-full max-w-lg relative" style={{ background: '#1E2329' }}>
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-bold" style={{ color: '#EAECEF' }}>
{editingModelId ? '编辑AI模型' : '添加AI模型'}
{editingModelId ? t('editAIModel', language) : t('addAIModel', language)}
</h3>
{editingModelId && (
<button
type="button"
onClick={() => {
if (confirm('确定要删除此AI模型配置吗')) {
if (confirm(t('confirmDeleteModel', language))) {
onDelete(editingModelId);
}
}}
className="p-2 rounded hover:bg-red-100 transition-colors"
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }}
title="删除配置"
title={t('deleteConfigFailed', language)}
>
<Trash2 className="w-4 h-4" />
</button>
)}
</div>
<form onSubmit={handleSubmit} className="space-y-4">
{!editingModelId && (
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}>
AI模型
{t('selectModel', language)}
</label>
<select
value={selectedModelId}
@@ -996,7 +1007,7 @@ function ModelConfigModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
>
<option value=""></option>
<option value="">{t('pleaseSelectModel', language)}</option>
{availableModels.map(model => (
<option key={model.id} value={model.id}>
{getShortName(model.name)} ({model.provider})
@@ -1040,7 +1051,7 @@ function ModelConfigModal({
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder="输入API密钥"
placeholder={t('enterAPIKey', language)}
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
@@ -1049,29 +1060,46 @@ function ModelConfigModal({
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}>
Base URL ()
{t('customBaseURL', language)}
</label>
<input
type="url"
value={baseUrl}
onChange={(e) => setBaseUrl(e.target.value)}
placeholder="自定义API基础URL如: https://api.openai.com/v1"
placeholder={t('customBaseURLPlaceholder', language)}
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
使API地址
{t('leaveBlankForDefault', language)}
</div>
</div>
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}>
Model Name ()
</label>
<input
type="text"
value={modelName}
onChange={(e) => setModelName(e.target.value)}
placeholder="例如: deepseek-chat, qwen-plus, gpt-4"
className="w-full px-3 py-2 rounded"
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
/>
<div className="text-xs mt-1" style={{ color: '#848E9C' }}>
使
</div>
</div>
<div className="p-4 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
<div className="text-sm font-semibold mb-2" style={{ color: '#F0B90B' }}>
{t('information', language)}
</div>
<div className="text-xs space-y-1" style={{ color: '#848E9C' }}>
<div> API Key将被加密存储</div>
<div> Base URL用于自定义API服务器地址</div>
<div> 使</div>
<div>{t('modelConfigInfo1', language)}</div>
<div>{t('modelConfigInfo2', language)}</div>
<div>{t('modelConfigInfo3', language)}</div>
</div>
</div>
</>
@@ -1084,7 +1112,7 @@ function ModelConfigModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold"
style={{ background: '#2B3139', color: '#848E9C' }}
>
{t('cancel', language)}
</button>
<button
type="submit"
@@ -1092,7 +1120,7 @@ function ModelConfigModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold disabled:opacity-50"
style={{ background: '#F0B90B', color: '#000' }}
>
{t('saveConfig', language)}
</button>
</div>
</form>
@@ -1184,30 +1212,30 @@ function ExchangeConfigModal({
<div className="bg-gray-800 rounded-lg p-6 w-full max-w-lg relative" style={{ background: '#1E2329' }}>
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-bold" style={{ color: '#EAECEF' }}>
{editingExchangeId ? '编辑交易所' : '添加交易所'}
{editingExchangeId ? t('editExchange', language) : t('addExchange', language)}
</h3>
{editingExchangeId && (
<button
type="button"
onClick={() => {
if (confirm('确定要删除此交易所配置吗?')) {
if (confirm(t('confirmDeleteExchange', language))) {
onDelete(editingExchangeId);
}
}}
className="p-2 rounded hover:bg-red-100 transition-colors"
style={{ background: 'rgba(246, 70, 93, 0.1)', color: '#F6465D' }}
title="删除配置"
title={t('deleteConfigFailed', language)}
>
<Trash2 className="w-4 h-4" />
</button>
)}
</div>
<form onSubmit={handleSubmit} className="space-y-4">
{!editingExchangeId && (
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: '#EAECEF' }}>
{t('selectExchange', language)}
</label>
<select
value={selectedExchangeId}
@@ -1216,7 +1244,7 @@ function ExchangeConfigModal({
style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }}
required
>
<option value=""></option>
<option value="">{t('pleaseSelectExchange', language)}</option>
{availableExchanges.map(exchange => (
<option key={exchange.id} value={exchange.id}>
{getShortName(exchange.name)} ({exchange.type.toUpperCase()})
@@ -1405,12 +1433,12 @@ function ExchangeConfigModal({
<div className="p-4 rounded" style={{ background: 'rgba(240, 185, 11, 0.1)', border: '1px solid rgba(240, 185, 11, 0.2)' }}>
<div className="text-sm font-semibold mb-2" style={{ color: '#F0B90B' }}>
{t('securityWarning', language)}
{t('securityWarning', language)}
</div>
<div className="text-xs space-y-1" style={{ color: '#848E9C' }}>
<div>{t('securityTip1', language)}</div>
<div>{t('securityTip2', language)}</div>
<div>{t('securityTip3', language)}</div>
<div>{t('exchangeConfigWarning1', language)}</div>
<div>{t('exchangeConfigWarning2', language)}</div>
<div>{t('exchangeConfigWarning3', language)}</div>
</div>
</div>
</>
@@ -1438,7 +1466,7 @@ function ExchangeConfigModal({
className="flex-1 px-4 py-2 rounded text-sm font-semibold disabled:opacity-50"
style={{ background: '#F0B90B', color: '#000' }}
>
{t('saveConfiguration', language)}
{t('saveConfig', language)}
</button>
</div>
</form>

View File

@@ -72,7 +72,7 @@ export function TraderConfigModal({
} else if (!isEditMode) {
setFormData({
trader_name: '',
ai_model: availableModels[0]?.id || '',
ai_model: availableModels[0]?.provider || '',
exchange_id: availableExchanges[0]?.id || '',
btc_eth_leverage: 5,
altcoin_leverage: 3,
@@ -217,7 +217,7 @@ export function TraderConfigModal({
className="w-full px-3 py-2 bg-[#0B0E11] border border-[#2B3139] rounded text-[#EAECEF] focus:border-[#F0B90B] focus:outline-none"
>
{availableModels.map(model => (
<option key={model.id} value={model.id}>
<option key={model.id} value={model.provider}>
{getShortName(model.name || model.id).toUpperCase()}
</option>
))}

View File

@@ -8,8 +8,13 @@ export const translations = {
aiTraders: 'AI Traders',
details: 'Details',
tradingPanel: 'Trading Panel',
competition: 'Competition',
running: 'RUNNING',
stopped: 'STOPPED',
adminMode: 'Admin Mode',
logout: 'Logout',
switchTrader: 'Switch Trader:',
view: 'View',
// Footer
footerTitle: 'NOFX - AI Trading System',
@@ -75,11 +80,14 @@ export const translations = {
aiCompetition: 'AI Competition',
traders: 'traders',
liveBattle: 'Live Battle',
realTimeBattle: 'Real-time Battle',
leader: 'Leader',
leaderboard: 'Leaderboard',
live: 'LIVE',
realTime: 'LIVE',
performanceComparison: 'Performance Comparison',
realTimePnL: 'Real-time PnL %',
realTimePnLPercent: 'Real-time PnL %',
headToHead: 'Head-to-Head Battle',
leadingBy: 'Leading by {gap}%',
behindBy: 'Behind by {gap}%',
@@ -212,6 +220,58 @@ export const translations = {
loading: 'Loading...',
loadingError: '⚠️ Failed to load AI learning data',
noCompleteData: 'No complete trading data (needs to complete open → close cycle)',
// AI Traders Page - Additional
inUse: 'In Use',
noModelsConfigured: 'No configured AI models',
noExchangesConfigured: 'No configured exchanges',
signalSource: 'Signal Source',
signalSourceConfig: 'Signal Source Configuration',
coinPoolDescription: 'API endpoint for coin pool data, leave blank to disable this signal source',
oiTopDescription: 'API endpoint for open interest rankings, leave blank to disable this signal source',
information: 'Information',
signalSourceInfo1: '• Signal source configuration is per-user, each user can set their own URLs',
signalSourceInfo2: '• When creating traders, you can choose whether to use these signal sources',
signalSourceInfo3: '• Configured URLs will be used to fetch market data and trading signals',
editAIModel: 'Edit AI Model',
addAIModel: 'Add AI Model',
confirmDeleteModel: 'Are you sure you want to delete this AI model configuration?',
selectModel: 'Select AI Model',
pleaseSelectModel: 'Please select a model',
customBaseURL: 'Base URL (Optional)',
customBaseURLPlaceholder: 'Custom API base URL, e.g.: https://api.openai.com/v1',
leaveBlankForDefault: 'Leave blank to use default API address',
modelConfigInfo1: '• API Key will be encrypted and stored, please ensure it is valid',
modelConfigInfo2: '• Base URL is used for custom API server address',
modelConfigInfo3: '• After deleting configuration, traders using this model will not work properly',
saveConfig: 'Save Configuration',
editExchange: 'Edit Exchange',
addExchange: 'Add Exchange',
confirmDeleteExchange: 'Are you sure you want to delete this exchange configuration?',
pleaseSelectExchange: 'Please select an exchange',
enterSecretKey: 'Enter secret key',
enterPassphrase: 'Enter Passphrase (Required for OKX)',
testnetDescription: 'Enable to connect to exchange test environment for simulated trading',
securityWarning: 'Security Warning',
exchangeConfigWarning1: '• API keys will be encrypted, recommend using read-only or futures trading permissions',
exchangeConfigWarning2: '• Do not grant withdrawal permissions to ensure fund security',
exchangeConfigWarning3: '• After deleting configuration, related traders will not be able to trade',
edit: 'Edit',
// Error Messages
createTraderFailed: 'Failed to create trader',
getTraderConfigFailed: 'Failed to get trader configuration',
modelConfigNotExist: 'Model configuration does not exist or is not enabled',
exchangeConfigNotExist: 'Exchange configuration does not exist or is not enabled',
updateTraderFailed: 'Failed to update trader',
deleteTraderFailed: 'Failed to delete trader',
operationFailed: 'Operation failed',
deleteConfigFailed: 'Failed to delete configuration',
modelNotExist: 'Model does not exist',
saveConfigFailed: 'Failed to save configuration',
exchangeNotExist: 'Exchange does not exist',
deleteExchangeConfigFailed: 'Failed to delete exchange configuration',
saveSignalSourceFailed: 'Failed to save signal source configuration',
// Login & Register
login: 'Sign In',
@@ -272,8 +332,13 @@ export const translations = {
aiTraders: 'AI交易员',
details: '详情',
tradingPanel: '交易面板',
competition: '竞赛',
running: '运行中',
stopped: '已停止',
adminMode: '管理员模式',
logout: '退出',
switchTrader: '切换交易员:',
view: '查看',
// Footer
footerTitle: 'NOFX - AI交易系统',
@@ -339,11 +404,14 @@ export const translations = {
aiCompetition: 'AI竞赛',
traders: '交易员',
liveBattle: '实时对战',
realTimeBattle: '实时对战',
leader: '领先者',
leaderboard: '排行榜',
live: '实时',
realTime: '实时',
performanceComparison: '表现对比',
realTimePnL: '实时收益率',
realTimePnLPercent: '实时收益率',
headToHead: '正面对决',
leadingBy: '领先 {gap}%',
behindBy: '落后 {gap}%',
@@ -476,6 +544,58 @@ export const translations = {
loading: '加载中...',
loadingError: '⚠️ 加载AI学习数据失败',
noCompleteData: '暂无完整交易数据(需要完成开仓→平仓的完整周期)',
// AI Traders Page - Additional
inUse: '正在使用',
noModelsConfigured: '暂无已配置的AI模型',
noExchangesConfigured: '暂无已配置的交易所',
signalSource: '信号源',
signalSourceConfig: '信号源配置',
coinPoolDescription: '用于获取币种池数据的API地址留空则不使用此信号源',
oiTopDescription: '用于获取持仓量排行数据的API地址留空则不使用此信号源',
information: '说明',
signalSourceInfo1: '• 信号源配置为用户级别每个用户可以设置自己的信号源URL',
signalSourceInfo2: '• 在创建交易员时可以选择是否使用这些信号源',
signalSourceInfo3: '• 配置的URL将用于获取市场数据和交易信号',
editAIModel: '编辑AI模型',
addAIModel: '添加AI模型',
confirmDeleteModel: '确定要删除此AI模型配置吗',
selectModel: '选择AI模型',
pleaseSelectModel: '请选择模型',
customBaseURL: 'Base URL (可选)',
customBaseURLPlaceholder: '自定义API基础URL如: https://api.openai.com/v1',
leaveBlankForDefault: '留空则使用默认API地址',
modelConfigInfo1: '• API Key将被加密存储请确保密钥有效',
modelConfigInfo2: '• Base URL用于自定义API服务器地址',
modelConfigInfo3: '• 删除配置后,使用此模型的交易员将无法正常工作',
saveConfig: '保存配置',
editExchange: '编辑交易所',
addExchange: '添加交易所',
confirmDeleteExchange: '确定要删除此交易所配置吗?',
pleaseSelectExchange: '请选择交易所',
enterSecretKey: '输入密钥',
enterPassphrase: '输入Passphrase (OKX必填)',
testnetDescription: '启用后将连接到交易所测试环境,用于模拟交易',
securityWarning: '安全提示',
exchangeConfigWarning1: '• API密钥将被加密存储建议使用只读或期货交易权限',
exchangeConfigWarning2: '• 不要授予提现权限,确保资金安全',
exchangeConfigWarning3: '• 删除配置后,相关交易员将无法正常交易',
edit: '编辑',
// Error Messages
createTraderFailed: '创建交易员失败',
getTraderConfigFailed: '获取交易员配置失败',
modelConfigNotExist: 'AI模型配置不存在或未启用',
exchangeConfigNotExist: '交易所配置不存在或未启用',
updateTraderFailed: '更新交易员失败',
deleteTraderFailed: '删除交易员失败',
operationFailed: '操作失败',
deleteConfigFailed: '删除配置失败',
modelNotExist: '模型不存在',
saveConfigFailed: '保存配置失败',
exchangeNotExist: '交易所不存在',
deleteExchangeConfigFailed: '删除交易所配置失败',
saveSignalSourceFailed: '保存信号源配置失败',
// Login & Register
login: '登录',

View File

@@ -101,6 +101,7 @@ export interface AIModel {
enabled: boolean;
apiKey?: string;
customApiUrl?: string;
customModelName?: string;
}
export interface Exchange {
@@ -140,6 +141,7 @@ export interface UpdateModelConfigRequest {
enabled: boolean;
api_key: string;
custom_api_url?: string;
custom_model_name?: string;
};
};
}