Merge pull request #331 from Icyoung/beta

Beta
This commit is contained in:
Icyoung
2025-11-03 21:15:38 +08:00
committed by GitHub
23 changed files with 4913 additions and 223 deletions

View File

@@ -6,42 +6,162 @@ labels: bug
assignees: ''
---
> **⚠️ Before submitting:** Please check the [Troubleshooting Guide](../../docs/guides/TROUBLESHOOTING.md) ([中文版](../../docs/guides/TROUBLESHOOTING.zh-CN.md)) to see if your issue can be resolved quickly.
## 🐛 Bug Description
<!-- A clear and concise description of what the bug is -->
## 🔍 Bug Category
<!-- Check the category that best describes this bug -->
- [ ] Trading execution (orders not executing, wrong position size, etc.)
- [ ] AI decision issues (unexpected decisions, only opening one direction, etc.)
- [ ] Exchange connection (API errors, authentication failures, etc.)
- [ ] UI/Frontend (display issues, buttons not working, data not updating, etc.)
- [ ] Backend/API (server errors, crashes, performance issues, etc.)
- [ ] Configuration (settings not saving, database errors, etc.)
- [ ] Other: _________________
## 📋 Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. Run command '...'
2. Click on '...' / Run command '...'
3. Configure '...'
4. See error
## ✅ Expected Behavior
<!-- What you expected to happen -->
## ❌ Actual Behavior
<!-- What actually happened -->
## 📸 Screenshots / Logs
<!-- If applicable, add screenshots or error logs to help explain your problem -->
## 📸 Screenshots & Logs
### Frontend Error (if applicable)
<!-- How to capture frontend errors: -->
<!-- 1. Open browser DevTools (F12 or Right-click → Inspect) -->
<!-- 2. Go to "Console" tab to see JavaScript errors -->
<!-- 3. Screenshot the error messages -->
<!-- 4. Check "Network" tab for failed API requests (show status code & response) -->
**Browser Console Screenshot:**
<!-- Paste screenshot here -->
**Network Tab (failed requests):**
<!-- Paste screenshot of failed API calls here -->
### Backend Logs (if applicable)
<!-- How to capture backend logs: -->
**Docker users:**
```bash
# View backend logs
docker compose logs backend --tail=100
# OR continuously follow logs
docker compose logs -f backend
```
Paste logs here
**Manual/PM2 users:**
```bash
# Terminal output where you ran: ./nofx
# OR PM2 logs:
pm2 logs nofx --lines 100
```
**Backend Log Output:**
```
Paste backend logs here (last 50-100 lines around the error)
```
### Trading/Decision Logs (if trading issue)
<!-- Decision logs are saved in: decision_logs/{trader_id}/ -->
<!-- Find the latest JSON file and paste relevant parts -->
**Decision Log Path:** `decision_logs/{trader_id}/{timestamp}.json`
```json
{
"paste relevant decision log here if applicable"
}
```
## 💻 Environment
**System:**
- **OS:** [e.g. macOS 13, Ubuntu 22.04, Windows 11]
- **Go Version:** [e.g. 1.21.5]
- **Node.js Version:** [e.g. 18.17.0]
- **NOFX Version/Commit:** [e.g. v3.0.0 or commit hash]
- **Deployment Method:** [Docker / Manual / PM2]
- **Deployment:** [Docker / Manual / PM2]
**Backend:**
- **Go Version:** [run: `go version`]
- **NOFX Version:** [run: `git log -1 --oneline` or check release tag]
**Frontend:**
- **Browser:** [e.g. Chrome 120, Firefox 121, Safari 17]
- **Node.js Version:** [run: `node -v`]
**Trading Setup:**
- **Exchange:** [Binance / Hyperliquid / Aster]
- **Account Type:** [Main Account / Subaccount]
- **Position Mode:** [Hedge Mode (Dual) / One-way Mode] ← **Important for trading bugs!**
- **AI Model:** [DeepSeek / Qwen / Custom]
- **Number of Traders:** [e.g. 1, 2, etc.]
## 🔧 Additional Context
<!-- Add any other context about the problem here -->
## 🔧 Configuration (if relevant)
<!-- Only include non-sensitive parts of your config -->
<!-- ⚠️ NEVER paste API keys or private keys! -->
- Does this happen consistently or intermittently?
- Did it work before? When did it break?
- Any recent configuration changes?
**Leverage Settings:**
```json
{
"btc_eth_leverage": 5,
"altcoin_leverage": 5
}
```
## ✋ Possible Solution
<!-- Optional: If you have ideas on how to fix this -->
**Any custom settings:**
<!-- e.g. modified scan_interval, custom coin list, etc. -->
## 📊 Additional Context
**Frequency:**
- [ ] Happens every time
- [ ] Happens randomly
- [ ] Happened once
**Timeline:**
- Did this work before? [ ] Yes [ ] No
- When did it break? [e.g. after upgrade to v3.0.0, after changing config, etc.]
- Recent changes? [e.g. updated dependencies, changed exchange, etc.]
**Impact:**
- [ ] System cannot start
- [ ] Trading stopped/broken
- [ ] UI broken but trading works
- [ ] Minor visual issue
- [ ] Other: _________________
## 💡 Possible Solution
<!-- Optional: If you have ideas on how to fix this, or workarounds you've tried -->
---
## 📝 Quick Tips for Faster Resolution
**For Trading Issues:**
1. ✅ Check Binance position mode: Go to Futures → ⚙️ Preferences → Position Mode → Must be **Hedge Mode**
2. ✅ Verify API permissions: Futures trading must be enabled
3. ✅ Check decision logs in `decision_logs/{trader_id}/` for AI reasoning
**For Connection Issues:**
4. ✅ Test API connectivity: `curl http://localhost:8080/api/health`
5. ✅ Check API rate limits on exchange
6. ✅ Verify API keys are not expired
**For UI Issues:**
7. ✅ Hard refresh: Ctrl+Shift+R (or Cmd+Shift+R on Mac)
8. ✅ Check browser console (F12) for errors
9. ✅ Verify backend is running: `docker compose ps` or `ps aux | grep nofx`

322
.github/PR_TITLE_GUIDE.md vendored Normal file
View File

@@ -0,0 +1,322 @@
# PR 标题指南
## 📋 概述
我们使用 **Conventional Commits** 格式来保持 PR 标题的一致性,但这是**建议性的**,不会阻止你的 PR 被合并。
## ✅ 推荐格式
```
type(scope): description
```
### 示例
```
feat(trader): add new trading strategy
fix(api): resolve authentication issue
docs: update README
chore(deps): update dependencies
ci(workflow): improve GitHub Actions
```
---
## 📖 详细说明
### Type类型- 必需
描述这次变更的类型:
| Type | 说明 | 示例 |
|------|------|------|
| `feat` | 新功能 | `feat(trader): add stop-loss feature` |
| `fix` | Bug 修复 | `fix(api): handle null response` |
| `docs` | 文档变更 | `docs: update installation guide` |
| `style` | 代码格式(不影响代码运行) | `style: format code with prettier` |
| `refactor` | 重构(既不是新功能也不是修复) | `refactor(exchange): simplify connection logic` |
| `perf` | 性能优化 | `perf(ai): optimize prompt processing` |
| `test` | 添加或修改测试 | `test(trader): add unit tests` |
| `chore` | 构建过程或辅助工具的变动 | `chore: update dependencies` |
| `ci` | CI/CD 相关变更 | `ci: add test coverage report` |
| `security` | 安全相关修复 | `security: update vulnerable dependencies` |
| `build` | 构建系统或外部依赖项变更 | `build: upgrade webpack to v5` |
### Scope范围- 可选
描述这次变更影响的范围:
| Scope | 说明 |
|-------|------|
| `exchange` | 交易所相关 |
| `trader` | 交易员/交易策略 |
| `ai` | AI 模型相关 |
| `api` | API 接口 |
| `ui` | 用户界面 |
| `frontend` | 前端代码 |
| `backend` | 后端代码 |
| `security` | 安全相关 |
| `deps` | 依赖项 |
| `workflow` | GitHub Actions workflows |
| `github` | GitHub 配置 |
| `actions` | GitHub Actions |
| `config` | 配置文件 |
| `docker` | Docker 相关 |
| `build` | 构建相关 |
| `release` | 发布相关 |
**注意:** 如果变更影响多个范围,可以省略 scope 或选择最主要的。
### Description描述- 必需
- 使用现在时态("add" 而不是 "added"
- 首字母小写
- 结尾不加句号
- 简洁明了地描述变更内容
---
## 🎯 完整示例
### ✅ 好的 PR 标题
```
feat(trader): add risk management system
fix(exchange): resolve connection timeout issue
docs: add API documentation for trading endpoints
style: apply consistent code formatting
refactor(ai): simplify prompt processing logic
perf(backend): optimize database queries
test(api): add integration tests for auth
chore(deps): update TypeScript to 5.0
ci(workflow): add automated security scanning
security(api): fix SQL injection vulnerability
build(docker): optimize Docker image size
```
### ⚠️ 需要改进的标题
| 不好的标题 | 问题 | 改进后 |
|-----------|------|--------|
| `update code` | 太模糊 | `refactor(trader): simplify order execution logic` |
| `Fixed bug` | 首字母大写,不够具体 | `fix(api): handle edge case in login` |
| `Add new feature.` | 有句号,不够具体 | `feat(ui): add dark mode toggle` |
| `changes` | 完全不符合格式 | `chore: update dependencies` |
| `feat: Added new trading algo` | 时态错误 | `feat(trader): add new trading algorithm` |
---
## 🤖 自动检查行为
### 当 PR 标题不符合格式时
1. **不会阻止合并**
- 检查会标记为"建议"
- PR 仍然可以被审查和合并
2. **会收到友好提示** 💬
- 机器人会在 PR 中留言
- 提供格式说明和示例
- 建议如何改进标题
3. **可以随时更新** 🔄
- 更新 PR 标题后会重新检查
- 无需关闭和重新打开 PR
### 示例评论
如果你的 PR 标题是 `update workflow`,你会收到这样的评论:
```markdown
## ⚠️ PR Title Format Suggestion
Your PR title doesn't follow the Conventional Commits format,
but this won't block your PR from being merged.
**Current title:** `update workflow`
**Recommended format:** `type(scope): description`
### Valid types:
feat, fix, docs, style, refactor, perf, test, chore, ci, security, build
### Common scopes (optional):
exchange, trader, ai, api, ui, frontend, backend, security, deps,
workflow, github, actions, config, docker, build, release
### Examples:
- feat(trader): add new trading strategy
- fix(api): resolve authentication issue
- docs: update README
- chore(deps): update dependencies
- ci(workflow): improve GitHub Actions
**Note:** This is a suggestion to improve consistency.
Your PR can still be reviewed and merged.
```
---
## 🔧 配置详情
### 支持的 Types
`.github/workflows/pr-checks.yml` 中配置:
```yaml
types: |
feat
fix
docs
style
refactor
perf
test
chore
ci
security
build
```
### 支持的 Scopes
```yaml
scopes: |
exchange
trader
ai
api
ui
frontend
backend
security
deps
workflow
github
actions
config
docker
build
release
```
### 添加新的 Scope
如果你需要添加新的 scope
1.`.github/workflows/pr-checks.yml``scopes` 部分添加
2.`.github/workflows/pr-checks-run.yml` 更新正则表达式(可选)
3. 更新本文档
---
## 📚 为什么使用 Conventional Commits
### 优点
1. **自动化 Changelog** 📝
- 可以自动生成版本更新日志
- 清晰地分类各种变更
2. **语义化版本** 🔢
- `feat` → MINOR 版本1.1.0
- `fix` → PATCH 版本1.0.1
- `BREAKING CHANGE` → MAJOR 版本2.0.0
3. **更好的可读性** 👀
- 一眼看出 PR 的目的
- 更容易浏览 Git 历史
4. **团队协作** 🤝
- 统一的提交风格
- 降低沟通成本
### 示例:自动生成的 Changelog
```markdown
## v1.2.0 (2025-11-02)
### Features
- **trader**: add risk management system (#123)
- **ui**: add dark mode toggle (#125)
### Bug Fixes
- **api**: resolve authentication issue (#124)
- **exchange**: fix connection timeout (#126)
### Documentation
- update API documentation (#127)
```
---
## 🎓 学习资源
- **Conventional Commits 官网:** https://www.conventionalcommits.org/
- **Angular Commit Guidelines:** https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit
- **Semantic Versioning:** https://semver.org/
---
## ❓ FAQ
### Q: 我必须遵循这个格式吗?
**A:** 不必须。这是建议性的,不会阻止你的 PR 被合并。但遵循格式可以提高项目的可维护性。
### Q: 如果我忘记了怎么办?
**A:** 机器人会在 PR 中提醒你,你可以随时更新标题。
### Q: 我可以在一个 PR 中做多种类型的变更吗?
**A:** 可以,但建议:
- 选择最主要的类型
- 或者考虑拆分成多个 PR更易于审查
### Q: Scope 可以省略吗?
**A:** 可以。`requireScope: false` 表示 scope 是可选的。
示例:`docs: update README` (没有 scope 也可以)
### Q: 我想添加新的 type 或 scope怎么做
**A:** 提一个 PR 修改 `.github/workflows/pr-checks.yml`,并在本文档中说明新增项的用途。
### Q: Breaking Changes 怎么表示?
**A:** 在描述中添加 `BREAKING CHANGE:` 或在 type 后加 `!`
```
feat!: remove deprecated API
feat(api)!: change authentication method
BREAKING CHANGE: The old /auth endpoint is removed
```
---
## 📊 统计
想看项目的 commit 类型分布?运行:
```bash
git log --oneline --no-merges | \
grep -oE '^[a-f0-9]+ (feat|fix|docs|style|refactor|perf|test|chore|ci|security|build)' | \
cut -d' ' -f2 | sort | uniq -c | sort -rn
```
---
## ✅ 快速检查清单
在提交 PR 前,检查你的标题是否:
- [ ] 包含有效的 typefeat, fix, docs 等)
- [ ] 使用小写字母开头
- [ ] 使用现在时态("add" 而不是 "added"
- [ ] 简洁明了(最好在 50 字符内)
- [ ] 准确描述了变更内容
**记住:** 这些都是建议,不是强制要求!

View File

@@ -1,153 +1,288 @@
# Pull Request
# Pull Request | PR 提交
## 📝 Description
> **💡 提示 Tip:** 推荐 PR 标题格式 Recommended PR title format: `type(scope): description`
> 例如 Examples: `feat(trader): add new strategy` | `fix(api): resolve auth issue`
> 详情 Details: [PR Title Guide](./PR_TITLE_GUIDE.md)
---
## 📝 Description | 描述
<!-- Provide a brief summary of your changes -->
<!-- 简要描述你的变更 -->
## 🎯 Type of Change
**English:**
**中文:**
---
## 🎯 Type of Change | 变更类型
<!-- Mark the relevant option with an "x" -->
<!-- 在相关选项上打"x" -->
- [ ] 🐛 Bug fix (non-breaking change which fixes an issue)
- [ ] ✨ New feature (non-breaking change which adds functionality)
- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] 📝 Documentation update
- [ ] 🎨 Code style update (formatting, renaming)
- [ ] ♻️ Refactoring (no functional changes)
- [ ] ⚡ Performance improvement
- [ ] ✅ Test update
- [ ] 🔧 Build/config change
- [ ] 🐛 Bug fix | 修复 Bug不影响现有功能的修复
- [ ] ✨ New feature | 新功能(不影响现有功能的新增)
- [ ] 💥 Breaking change | 破坏性变更(会导致现有功能无法正常工作的修复或功能)
- [ ] 📝 Documentation update | 文档更新
- [ ] 🎨 Code style update | 代码样式更新(格式化、重命名等)
- [ ] ♻️ Refactoring | 重构(无功能变更)
- [ ] ⚡ Performance improvement | 性能优化
- [ ] ✅ Test update | 测试更新
- [ ] 🔧 Build/config change | 构建/配置变更
- [ ] 🔒 Security fix | 安全修复
## 🔗 Related Issues
---
## 🔗 Related Issues | 相关 Issue
<!-- Link related issues below. Use "Closes #123" to auto-close issues when PR is merged -->
<!-- 在下方关联相关 issue。使用 "Closes #123" 可以在 PR 合并时自动关闭 issue -->
- Closes #
- Related to #
- Closes # | 关闭 #
- Related to # | 相关 #
## 📋 Changes Made
---
## 📋 Changes Made | 具体变更
<!-- List the specific changes you made -->
<!-- 列出你做的具体变更 -->
**English:**
- Change 1
- Change 2
- Change 3
## 🧪 Testing
**中文:**
- 变更 1
- 变更 2
- 变更 3
### Manual Testing
---
## 🧪 Testing | 测试
### Manual Testing | 手动测试
<!-- Describe how you tested your changes -->
<!-- 描述你如何测试你的变更 -->
- [ ] Tested locally (manual verification)
- [ ] Tested on testnet (for exchange integrations)
- [ ] Tested with different configurations
- [ ] Verified no existing functionality broke
- [ ] Tested locally | 本地测试通过
- [ ] Tested on testnet | 测试网测试通过(交易所集成相关)
- [ ] Tested with different configurations | 测试了不同配置
- [ ] Verified no existing functionality broke | 确认没有破坏现有功能
### Test Environment
### Test Environment | 测试环境
- **OS:** [e.g. macOS, Ubuntu]
- **Go Version:** [e.g. 1.21.5]
- **Exchange:** [if applicable]
- **OS | 操作系统:** [e.g. macOS, Ubuntu, Windows]
- **Go Version | Go 版本:** [e.g. 1.21.5]
- **Node Version | Node 版本:** [e.g. 18.x] (if applicable | 如适用)
- **Exchange | 交易所:** [if applicable | 如适用]
### Test Results
### Test Results | 测试结果
<!-- Paste relevant test output or describe results -->
<!-- 粘贴相关测试输出或描述结果 -->
```
Test output here
Test output here | 测试输出
```
## 📸 Screenshots / Demo
---
## 📸 Screenshots / Demo | 截图/演示
<!-- If applicable, add screenshots or video demo -->
<!-- 如适用,添加截图或视频演示 -->
<!-- For UI changes, include before/after screenshots -->
<!-- 对于 UI 变更,包含变更前后的截图 -->
**Before:**
**Before | 变更前:**
**After:**
**After | 变更后:**
## ✅ Checklist
---
## ✅ Checklist | 检查清单
<!-- Mark completed items with an "x" -->
<!-- 在已完成的项目上打"x" -->
### Code Quality
### Code Quality | 代码质量
- [ ] My code follows the project's code style ([Contributing Guide](../CONTRIBUTING.md))
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] My changes generate no new warnings or errors
- [ ] Code compiles successfully (`go build` / `npm run build`)
- [ ] I have run `go fmt` (for Go code)
- [ ] My code follows the project's code style | 我的代码遵循项目代码风格 ([Contributing Guide](../CONTRIBUTING.md))
- [ ] I have performed a self-review of my code | 我已进行代码自查
- [ ] I have commented my code, particularly in hard-to-understand areas | 我已添加代码注释,特别是难以理解的部分
- [ ] My changes generate no new warnings or errors | 我的变更没有产生新的警告或错误
- [ ] Code compiles successfully | 代码编译成功 (`go build` / `npm run build`)
- [ ] I have run `go fmt` (for Go code) | 我已运行 `go fmt`Go 代码)
- [ ] I have run `npm run lint` (for frontend code) | 我已运行 `npm run lint`(前端代码)
### Testing
### Testing | 测试
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally
- [ ] I have tested on testnet (for trading/exchange changes)
- [ ] I have added tests that prove my fix is effective or that my feature works | 我已添加证明修复有效或功能正常的测试
- [ ] New and existing unit tests pass locally | 新旧单元测试在本地通过
- [ ] I have tested on testnet (for trading/exchange changes) | 我已在测试网测试(交易/交易所变更)
- [ ] Integration tests pass | 集成测试通过
### Documentation
### Documentation | 文档
- [ ] I have updated the documentation accordingly
- [ ] I have updated the README if needed
- [ ] I have added inline code comments where necessary
- [ ] I have updated type definitions (for TypeScript changes)
- [ ] I have updated the documentation accordingly | 我已相应更新文档
- [ ] I have updated the README if needed | 我已更新 README如需要
- [ ] I have added inline code comments where necessary | 我已在必要处添加代码注释
- [ ] I have updated type definitions (for TypeScript changes) | 我已更新类型定义TypeScript 变更)
- [ ] I have updated API documentation (if applicable) | 我已更新 API 文档(如适用)
### Git
- [ ] My commits follow the conventional commits format (`feat:`, `fix:`, etc.)
- [ ] I have rebased my branch on the latest `dev` branch
- [ ] There are no merge conflicts
- [ ] My commits follow the conventional commits format | 我的提交遵循 Conventional Commits 格式 (`feat:`, `fix:`, etc.)
- [ ] I have rebased my branch on the latest `dev` branch | 我已将分支 rebase 到最新的 `dev` 分支
- [ ] There are no merge conflicts | 没有合并冲突
- [ ] Commit messages are clear and descriptive | 提交信息清晰明确
## 🔒 Security Considerations
---
## 🔒 Security Considerations | 安全考虑
<!-- Answer these questions for security-sensitive changes -->
<!-- 对于安全敏感的变更,请回答以下问题 -->
- [ ] No API keys or secrets are hardcoded
- [ ] User inputs are properly validated
- [ ] No SQL injection vulnerabilities introduced
- [ ] Authentication/authorization properly handled
- [ ] N/A (not security-related)
- [ ] No API keys or secrets are hardcoded | 没有硬编码 API 密钥或密钥
- [ ] User inputs are properly validated | 用户输入已正确验证
- [ ] No SQL injection vulnerabilities introduced | 未引入 SQL 注入漏洞
- [ ] No XSS vulnerabilities introduced | 未引入 XSS 漏洞
- [ ] Authentication/authorization properly handled | 认证/授权已正确处理
- [ ] Sensitive data is encrypted | 敏感数据已加密
- [ ] N/A (not security-related) | 不适用(非安全相关)
## ⚡ Performance Impact
---
## ⚡ Performance Impact | 性能影响
<!-- Describe any performance implications -->
<!-- 描述任何性能影响 -->
- [ ] No significant performance impact
- [ ] Performance improved
- [ ] Performance may be impacted (explain below)
- [ ] No significant performance impact | 无显著性能影响
- [ ] Performance improved | 性能提升
- [ ] Performance may be impacted (explain below) | 性能可能受影响(请在下方说明)
<!-- If performance impacted, explain: -->
<!-- 如果性能受影响,请说明: -->
## 📚 Additional Notes
**English:**
**中文:**
---
## 🌐 Internationalization | 国际化
<!-- For UI/documentation changes -->
<!-- 对于 UI/文档变更 -->
- [ ] All user-facing text supports i18n | 所有面向用户的文本支持国际化
- [ ] Both English and Chinese versions provided | 提供了中英文版本
- [ ] N/A | 不适用
---
## 📚 Additional Notes | 补充说明
<!-- Any additional information for reviewers -->
<!-- 给审查者的任何补充信息 -->
**English:**
**中文:**
---
## For Bounty Claims
## 💰 For Bounty Claims | 赏金申请
<!-- Fill this section only if claiming a bounty -->
<!-- 仅在申请赏金时填写此部分 -->
- [ ] This PR is for bounty issue #
- [ ] All acceptance criteria from the bounty issue are met
- [ ] I have included a demo video/screenshots
- [ ] I am ready for payment upon merge
- [ ] This PR is for bounty issue # | 此 PR 用于赏金 issue #
- [ ] All acceptance criteria from the bounty issue are met | 满足赏金 issue 的所有验收标准
- [ ] I have included a demo video/screenshots | 我已包含演示视频/截图
- [ ] I am ready for payment upon merge | 我准备好在合并后接收付款
**Payment Details:** <!-- Discuss privately with maintainers -->
**Payment Details | 付款详情:** <!-- Discuss privately with maintainers | 与维护者私下讨论 -->
---
## 🙏 Reviewer Notes
## 🙏 Reviewer Notes | 审查者注意事项
<!-- Optional: anything specific you want reviewers to focus on? -->
<!-- 可选:你希望审查者关注的特定内容? -->
**English:**
**中文:**
---
## 📋 PR Size Estimate | PR 大小估计
<!-- This helps reviewers plan their time -->
<!-- 这有助于审查者安排时间 -->
- [ ] 🟢 Small (< 100 lines) | < 100
- [ ] 🟡 Medium (100-500 lines) | 100-500
- [ ] 🔴 Large (> 500 lines) | 大(> 500 行)
<!-- For large PRs, consider: -->
<!-- 对于大型 PR考虑 -->
<!-- - Breaking into smaller, focused PRs | 拆分为更小、更专注的 PR -->
<!-- - Providing a detailed explanation | 提供详细说明 -->
<!-- - Highlighting the most important changes | 突出最重要的变更 -->
---
## 🎯 Review Focus Areas | 审查重点
<!-- Help reviewers know where to focus their attention -->
<!-- 帮助审查者了解重点关注的地方 -->
Please pay special attention to:
请特别注意:
- [ ] Logic changes | 逻辑变更
- [ ] Security implications | 安全影响
- [ ] Performance optimization | 性能优化
- [ ] API changes | API 变更
- [ ] Database schema changes | 数据库架构变更
- [ ] UI/UX changes | UI/UX 变更
---
**By submitting this PR, I confirm that:**
- [ ] I have read the [Contributing Guidelines](../CONTRIBUTING.md)
- [ ] I agree to the [Code of Conduct](../CODE_OF_CONDUCT.md)
- [ ] My contribution is licensed under the MIT License
**提交此 PR我确认**
- [ ] I have read the [Contributing Guidelines](../CONTRIBUTING.md) | 我已阅读[贡献指南](../CONTRIBUTING.md)
- [ ] I agree to the [Code of Conduct](../CODE_OF_CONDUCT.md) | 我同意[行为准则](../CODE_OF_CONDUCT.md)
- [ ] My contribution is licensed under the MIT License | 我的贡献遵循 MIT 许可证
- [ ] I understand this is a voluntary contribution | 我理解这是自愿贡献
- [ ] I have the right to submit this code | 我有权提交此代码
---
<!--
🌟 感谢你的贡献Thank you for your contribution!
贡献者来自世界各地,我们重视每一份贡献。
Contributors come from all around the world, and we value every contribution.
如果你是首次贡献,欢迎加入我们的社区!
If this is your first contribution, welcome to our community!
💬 需要帮助Feel free to ask questions in:
- GitHub Discussions
- Discord: [链接 Link]
- Telegram: [链接 Link]
-->

176
.github/workflows/README.md vendored Normal file
View File

@@ -0,0 +1,176 @@
# GitHub Actions Workflows
This directory contains the GitHub Actions workflows for the NOFX project.
## 📚 Documentation Index
- **[README.md](./README.md)** - This file, overview of all workflows
- **[PERMISSIONS.md](./PERMISSIONS.md)** - Detailed permission analysis and security model
- **[TRIGGERS.md](./TRIGGERS.md)** - Comparison of event triggers (pull_request vs pull_request_target vs workflow_run)
- **[FORK_PR_FLOW.md](./FORK_PR_FLOW.md)** - Complete analysis of what happens when a fork PR is submitted
- **[FLOW_DIAGRAM.md](./FLOW_DIAGRAM.md)** - Visual flow diagrams and quick reference
- **[SECRETS_SCANNING.md](./SECRETS_SCANNING.md)** - Secrets scanning solutions and TruffleHog setup
## 🚀 Quick Start
**Want to understand how fork PRs work?** → Read [FLOW_DIAGRAM.md](./FLOW_DIAGRAM.md)
**Need security details?** → Read [PERMISSIONS.md](./PERMISSIONS.md)
**Confused about triggers?** → Read [TRIGGERS.md](./TRIGGERS.md)
## PR Check Workflows
We use a **two-workflow pattern** to safely handle PR checks from both internal and fork PRs:
### 1. `pr-checks-run.yml` - Execute Checks
**Trigger:** On pull request (opened, synchronize, reopened)
**Permissions:** Read-only
**Purpose:** Executes all PR checks with read-only permissions, making it safe for fork PRs.
**What it does:**
- ✅ Checks PR title format (Conventional Commits)
- ✅ Calculates PR size
- ✅ Runs backend checks (Go formatting, vet, tests)
- ✅ Runs frontend checks (linting, type checking, build)
- ✅ Saves all results as artifacts
**Security:** Safe for fork PRs because it only has read permissions and cannot access secrets or modify the repository.
### 2. `pr-checks-comment.yml` - Post Results
**Trigger:** When `pr-checks-run.yml` completes (workflow_run)
**Permissions:** Write (pull-requests, issues)
**Purpose:** Posts check results as PR comments, running in the main repository context.
**What it does:**
- ✅ Downloads artifacts from `pr-checks-run.yml`
- ✅ Reads check results
- ✅ Posts a comprehensive comment to the PR
**Security:** Safe because:
- Runs in the main repository context (not fork context)
- Has write permissions but doesn't execute untrusted code
- Only reads pre-generated results from artifacts
### 3. `pr-checks.yml` - Strict Checks
**Trigger:** On pull request
**Permissions:** Read + conditional write
**Purpose:** Runs mandatory checks that must pass before PR can be merged.
**What it does:**
- ✅ Validates PR title (blocks merge if invalid)
- ✅ Auto-labels PR based on size and files changed (non-fork only)
- ✅ Runs backend tests (Go)
- ✅ Runs frontend tests (React/TypeScript)
- ✅ Security scanning (Trivy, Gitleaks)
**Security:**
- Fork PRs: Only runs read-only operations (tests, security scans)
- Non-fork PRs: Can add labels and comments
- Uses `continue-on-error` for operations that may fail on forks
## Why Two Workflows for PR Checks?
### The Problem
When a PR comes from a forked repository:
- GitHub restricts `GITHUB_TOKEN` permissions for security
- Fork PRs cannot write comments, add labels, or access secrets
- This prevents malicious contributors from:
- Stealing repository secrets
- Modifying workflow files to execute malicious code
- Spamming issues/PRs with automated comments
### The Solution
**Two-Workflow Pattern:**
```
Fork PR Submitted
[pr-checks-run.yml]
- Runs with read-only permissions
- Executes all checks safely
- Saves results to artifacts
[pr-checks-comment.yml]
- Triggered by workflow_run
- Runs in main repo context (has write permissions)
- Downloads artifacts
- Posts comment with results
```
This approach:
- ✅ Allows fork PRs to run checks
- ✅ Safely posts results as comments
- ✅ Prevents security vulnerabilities
- ✅ Follows GitHub's best practices
### Can workflow_run Comment on Fork PRs?
**Yes! ✅ The permissions are sufficient.**
**Key Understanding:**
- `workflow_run` executes in the **base repository** context
- Fork PRs exist in the **base repository** (not in the fork)
- The base repository's `GITHUB_TOKEN` has write permissions
- Therefore, `workflow_run` can comment on fork PRs
**Security:**
- Fork PR code runs in isolated environment (read-only)
- Comment workflow doesn't execute fork code
- Only reads pre-generated artifact data
**For detailed permission analysis, see:** [PERMISSIONS.md](./PERMISSIONS.md)
## Workflow Comparison
| Workflow | Fork PRs | Write Access | Blocks Merge | Purpose |
|----------|----------|--------------|--------------|---------|
| `pr-checks-run.yml` | ✅ Yes | ❌ No | ❌ No | Advisory checks |
| `pr-checks-comment.yml` | ✅ Yes | ✅ Yes* | ❌ No | Post results |
| `pr-checks.yml` | ✅ Yes | ⚠️ Partial | ✅ Yes | Mandatory checks |
\* Write access only in main repo context, not available to fork PR code
## File History
- `pr-checks-advisory.yml.old` - Old advisory workflow that failed on fork PRs (deprecated)
- Now replaced by the two-workflow pattern (`pr-checks-run.yml` + `pr-checks-comment.yml`)
## Testing the Workflows
### Test with a Fork PR
1. Fork the repository
2. Make changes in your fork
3. Create a PR to the main repository
4. Observe:
- `pr-checks-run.yml` runs successfully with read-only access
- `pr-checks-comment.yml` posts results as a comment
- `pr-checks.yml` runs tests but skips labeling
### Test with a Branch PR
1. Create a branch in the main repository
2. Make changes
3. Create a PR
4. Observe:
- All workflows run with full permissions
- Labels are added automatically
- Comments are posted
## References
- [GitHub Actions: Keeping your GitHub Actions and workflows secure Part 1](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
- [Safely posting comments from untrusted workflows](https://securitylab.github.com/research/github-actions-building-blocks/)
- [GitHub Actions: workflow_run trigger](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run)

View File

@@ -9,7 +9,7 @@ on:
# Results will be posted as comments to help contributors improve their PRs
permissions:
contents: read
contents: write
pull-requests: write
checks: write
issues: write

248
.github/workflows/pr-checks-comment.yml vendored Normal file
View File

@@ -0,0 +1,248 @@
name: PR Checks - Comment
# This workflow posts ADVISORY check results as comments
# Runs in the main repo context with write permissions (SAFE)
# Triggered after pr-checks-run.yml completes
#
# NOTE: PR title and size checks are handled by pr-checks.yml (no duplication)
# This workflow only posts backend/frontend advisory check results
on:
workflow_run:
workflows: ["PR Checks - Run"]
types: [completed]
# Write permissions - SAFE because runs in main repo context
# This token has write access to the base repository
# Fork PRs exist in the base repo, so we can comment on them
permissions:
pull-requests: write
issues: write
actions: read # Needed to download artifacts
jobs:
comment:
name: Post Advisory Check Results
runs-on: ubuntu-latest
# Only run if the workflow was triggered by a pull_request event
if: github.event.workflow_run.event == 'pull_request'
steps:
- name: Download artifacts
id: download-artifacts
continue-on-error: true
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
path: artifacts
- name: Debug workflow run info
run: |
echo "=== Workflow Run Debug Info ==="
echo "Workflow Run ID: ${{ github.event.workflow_run.id }}"
echo "Workflow Run Event: ${{ github.event.workflow_run.event }}"
echo "Workflow Run Conclusion: ${{ github.event.workflow_run.conclusion }}"
echo "Workflow Run Head SHA: ${{ github.event.workflow_run.head_sha }}"
- name: List downloaded artifacts
run: |
echo "=== Checking downloaded artifacts ==="
ls -la artifacts/ || echo "⚠️ No artifacts directory found"
find artifacts/ -type f || echo "⚠️ No files found in artifacts"
echo ""
echo "Artifact download result: ${{ steps.download-artifacts.outcome }}"
- name: Read backend results
id: backend
continue-on-error: true
run: |
if [ -f artifacts/backend-results/backend-results.json ]; then
echo "=== Backend Results JSON ==="
cat artifacts/backend-results/backend-results.json
echo "pr_number=$(jq -r '.pr_number' artifacts/backend-results/backend-results.json)" >> $GITHUB_OUTPUT
echo "fmt_status=$(jq -r '.fmt_status' artifacts/backend-results/backend-results.json)" >> $GITHUB_OUTPUT
echo "vet_status=$(jq -r '.vet_status' artifacts/backend-results/backend-results.json)" >> $GITHUB_OUTPUT
echo "test_status=$(jq -r '.test_status' artifacts/backend-results/backend-results.json)" >> $GITHUB_OUTPUT
# Read output files
if [ -f artifacts/backend-results/fmt-files.txt ]; then
echo "fmt_files<<EOF" >> $GITHUB_OUTPUT
cat artifacts/backend-results/fmt-files.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
if [ -f artifacts/backend-results/vet-output-short.txt ]; then
echo "vet_output<<EOF" >> $GITHUB_OUTPUT
cat artifacts/backend-results/vet-output-short.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
if [ -f artifacts/backend-results/test-output-short.txt ]; then
echo "test_output<<EOF" >> $GITHUB_OUTPUT
cat artifacts/backend-results/test-output-short.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
else
echo "pr_number=0" >> $GITHUB_OUTPUT
echo "⚠️ Backend results artifact not found"
fi
- name: Read frontend results
id: frontend
continue-on-error: true
run: |
if [ -f artifacts/frontend-results/frontend-results.json ]; then
echo "=== Frontend Results JSON ==="
cat artifacts/frontend-results/frontend-results.json
echo "build_status=$(jq -r '.build_status' artifacts/frontend-results/frontend-results.json)" >> $GITHUB_OUTPUT
# Read output files
if [ -f artifacts/frontend-results/build-output-short.txt ]; then
echo "build_output<<EOF" >> $GITHUB_OUTPUT
cat artifacts/frontend-results/build-output-short.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
else
echo "⚠️ Frontend results artifact not found"
fi
- name: Post advisory results comment
if: steps.backend.outputs.pr_number != '0'
uses: actions/github-script@v7
with:
script: |
const prNumber = ${{ steps.backend.outputs.pr_number }};
let comment = '## 🤖 Advisory Check Results\n\n';
comment += 'These are **advisory** checks to help improve code quality. They won\'t block your PR from being merged.\n\n';
comment += '> **Note:** PR title and size checks are handled by the main workflow and may appear in a separate comment.\n\n';
// Backend checks
const fmtStatus = '${{ steps.backend.outputs.fmt_status }}';
const vetStatus = '${{ steps.backend.outputs.vet_status }}';
const testStatus = '${{ steps.backend.outputs.test_status }}';
if (fmtStatus || vetStatus || testStatus) {
comment += '\n### 🔧 Backend Checks\n\n';
if (fmtStatus) {
comment += '**Go Formatting:** ' + fmtStatus + '\n';
const fmtFiles = `${{ steps.backend.outputs.fmt_files }}`;
if (fmtFiles && fmtFiles.trim()) {
comment += '<details><summary>Files needing formatting</summary>\n\n```\n' + fmtFiles + '\n```\n</details>\n\n';
}
}
if (vetStatus) {
comment += '**Go Vet:** ' + vetStatus + '\n';
const vetOutput = `${{ steps.backend.outputs.vet_output }}`;
if (vetOutput && vetOutput.trim()) {
comment += '<details><summary>Issues found</summary>\n\n```\n' + vetOutput.substring(0, 1000) + '\n```\n</details>\n\n';
}
}
if (testStatus) {
comment += '**Tests:** ' + testStatus + '\n';
const testOutput = `${{ steps.backend.outputs.test_output }}`;
if (testOutput && testOutput.trim()) {
comment += '<details><summary>Test output</summary>\n\n```\n' + testOutput.substring(0, 1000) + '\n```\n</details>\n\n';
}
}
comment += '\n**Fix locally:**\n';
comment += '```bash\n';
comment += 'go fmt ./... # Format code\n';
comment += 'go vet ./... # Check for issues\n';
comment += 'go test ./... # Run tests\n';
comment += '```\n';
}
// Frontend checks
const buildStatus = '${{ steps.frontend.outputs.build_status }}';
if (buildStatus) {
comment += '\n### ⚛️ Frontend Checks\n\n';
comment += '**Build & Type Check:** ' + buildStatus + '\n';
const buildOutput = `${{ steps.frontend.outputs.build_output }}`;
if (buildOutput && buildOutput.trim()) {
comment += '<details><summary>Build output</summary>\n\n```\n' + buildOutput.substring(0, 1000) + '\n```\n</details>\n\n';
}
comment += '\n**Fix locally:**\n';
comment += '```bash\n';
comment += 'cd web\n';
comment += 'npm run build # Test build (includes type checking)\n';
comment += '```\n';
}
comment += '\n---\n\n';
comment += '### 📖 Resources\n\n';
comment += '- [Contributing Guidelines](https://github.com/tinkle-community/nofx/blob/dev/CONTRIBUTING.md)\n';
comment += '- [Migration Guide](https://github.com/tinkle-community/nofx/blob/dev/docs/community/MIGRATION_ANNOUNCEMENT.md)\n\n';
comment += '**Questions?** Feel free to ask in the comments! 🙏\n\n';
comment += '---\n\n';
comment += '*These checks are advisory and won\'t block your PR from being merged. This comment is automatically generated from [pr-checks-run.yml](https://github.com/tinkle-community/nofx/blob/dev/.github/workflows/pr-checks-run.yml).*';
// Post comment
await github.rest.issues.createComment({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
- name: Post fallback comment if no results
if: steps.backend.outputs.pr_number == '0'
uses: actions/github-script@v7
with:
script: |
// Try to get PR number from the workflow_run event
const pulls = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
head: `${context.repo.owner}:${{ github.event.workflow_run.head_branch }}`
});
if (pulls.data.length === 0) {
console.log('⚠️ Could not find PR for this workflow run');
return;
}
const prNumber = pulls.data[0].number;
const comment = [
'## ⚠️ Advisory Checks - Results Unavailable',
'',
'The advisory checks workflow completed, but results could not be retrieved.',
'',
'### Possible reasons:',
'- Artifacts were not uploaded successfully',
'- Artifacts expired (retention: 1 day)',
'- Permission issues',
'',
'### What to do:',
'1. Check the [PR Checks - Run workflow](${{ github.event.workflow_run.html_url }}) logs',
'2. Ensure your code passes local checks:',
'```bash',
'# Backend',
'go fmt ./...',
'go vet ./...',
'go build',
'go test ./...',
'',
'# Frontend (if applicable)',
'cd web',
'npm run build',
'```',
'',
'---',
'',
'*This is an automated fallback message. The advisory checks ran but results are not available.*'
].join('\n');
await github.rest.issues.createComment({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});

160
.github/workflows/pr-checks-run.yml vendored Normal file
View File

@@ -0,0 +1,160 @@
name: PR Checks - Run
# This workflow runs advisory PR checks with read-only permissions
# Safe for fork PRs - results are saved as artifacts
# Companion workflow (pr-checks-comment.yml) will post comments
#
# NOTE: This workflow provides ADVISORY checks (non-blocking)
# Main blocking checks are in pr-checks.yml
# PR title and size checks are handled by pr-checks.yml (no duplication)
on:
pull_request:
types: [opened, synchronize, reopened]
branches: [main, dev]
# Read-only permissions - safe for fork PRs
permissions:
contents: read
jobs:
# Backend advisory checks
# Different from pr-checks.yml: these use continue-on-error and generate reports
backend-checks:
name: Backend Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libta-lib-dev || true
go mod download || true
- name: Check Go formatting
id: go-fmt
continue-on-error: true
run: |
UNFORMATTED=$(gofmt -l . 2>/dev/null || echo "")
if [ -n "$UNFORMATTED" ]; then
echo "status=⚠️ Needs formatting" >> $GITHUB_OUTPUT
echo "$UNFORMATTED" | head -10 > fmt-files.txt
else
echo "status=✅ Good" >> $GITHUB_OUTPUT
echo "" > fmt-files.txt
fi
- name: Run go vet
id: go-vet
continue-on-error: true
run: |
if go vet ./... 2>&1 | tee vet-output.txt; then
echo "status=✅ Good" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Issues found" >> $GITHUB_OUTPUT
cat vet-output.txt | head -20 > vet-output-short.txt
fi
- name: Run tests
id: go-test
continue-on-error: true
run: |
if go test ./... -v 2>&1 | tee test-output.txt; then
echo "status=✅ Passed" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Failed" >> $GITHUB_OUTPUT
cat test-output.txt | tail -30 > test-output-short.txt
fi
- name: Save backend results
if: always()
run: |
cat > backend-results.json <<EOF
{
"pr_number": ${{ github.event.pull_request.number }},
"fmt_status": "${{ steps.go-fmt.outputs.status }}",
"vet_status": "${{ steps.go-vet.outputs.status }}",
"test_status": "${{ steps.go-test.outputs.status }}"
}
EOF
- name: Upload backend results
if: always()
uses: actions/upload-artifact@v4
with:
name: backend-results
path: |
backend-results.json
fmt-files.txt
vet-output-short.txt
test-output-short.txt
retention-days: 1
# Frontend advisory checks
# Different from pr-checks.yml: these use continue-on-error and generate reports
frontend-checks:
name: Frontend Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Check if web directory exists
id: check-web
run: |
if [ -d "web" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Install dependencies
if: steps.check-web.outputs.exists == 'true'
working-directory: ./web
continue-on-error: true
run: npm ci
- name: Build and Type Check
if: steps.check-web.outputs.exists == 'true'
id: build
working-directory: ./web
continue-on-error: true
run: |
# build script includes: tsc && vite build
if npm run build 2>&1 | tee build-output.txt; then
echo "status=✅ Success" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Failed" >> $GITHUB_OUTPUT
cat build-output.txt | tail -30 > build-output-short.txt
fi
- name: Save frontend results
if: always() && steps.check-web.outputs.exists == 'true'
working-directory: ./web
run: |
cat > frontend-results.json <<EOF
{
"pr_number": ${{ github.event.pull_request.number }},
"build_status": "${{ steps.build.outputs.status }}"
}
EOF
- name: Upload frontend results
if: always() && steps.check-web.outputs.exists == 'true'
uses: actions/upload-artifact@v4
with:
name: frontend-results
path: |
web/frontend-results.json
web/build-output-short.txt
retention-days: 1

View File

@@ -7,13 +7,24 @@ on:
- dev
- main
# Default permissions for all jobs
# Note: Fork PRs won't have write access for security
# Advisory checks use separate workflow (pr-checks-run.yml + pr-checks-comment.yml)
permissions:
contents: read # Read repository contents
pull-requests: write # Manage PRs (labels, comments) - only works for non-fork PRs
issues: write # Manage issues (PRs are issues) - only works for non-fork PRs
jobs:
# Validate PR title and description
validate-pr:
name: Validate PR Format
runs-on: ubuntu-latest
# Inherits workflow-level permissions (contents: read, pull-requests: write, issues: write)
steps:
- name: Check PR title format
id: semantic-pr
continue-on-error: true # Don't block PR if title format is invalid
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -29,6 +40,7 @@ jobs:
chore
ci
security
build
scopes: |
exchange
trader
@@ -39,10 +51,70 @@ jobs:
backend
security
deps
workflow
github
actions
config
docker
build
release
requireScope: false
- name: Comment on invalid PR title
if: steps.semantic-pr.outcome == 'failure'
uses: actions/github-script@v7
continue-on-error: true # Don't fail for fork PRs
with:
script: |
const prTitle = context.payload.pull_request.title;
const isFork = context.payload.pull_request.head.repo.full_name !== context.payload.pull_request.base.repo.full_name;
const comment = [
'## ⚠️ PR Title Format Suggestion',
'',
"Your PR title doesn't follow the Conventional Commits format, but **this won't block your PR from being merged**.",
'',
`**Current title:** \`${prTitle}\``,
'',
'**Recommended format:** `type(scope): description`',
'',
'### Valid types:',
'`feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `security`, `build`',
'',
'### Common scopes (optional):',
'`exchange`, `trader`, `ai`, `api`, `ui`, `frontend`, `backend`, `security`, `deps`, `workflow`, `github`, `actions`, `config`, `docker`, `build`, `release`',
'',
'### Examples:',
'- `feat(trader): add new trading strategy`',
'- `fix(api): resolve authentication issue`',
'- `docs: update README`',
'- `chore(deps): update dependencies`',
'- `ci(workflow): improve GitHub Actions`',
'',
'**Note:** This is a suggestion to improve consistency. Your PR can still be reviewed and merged.',
'',
'---',
'*This is an automated comment. You can update the PR title anytime.*'
].join('\n');
if (!isFork) {
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: comment
});
} catch (error) {
console.log('Could not post comment (expected for fork PRs):', error.message);
}
} else {
console.log('Fork PR - comment will be posted by pr-checks-comment.yml');
}
- name: Check PR size
uses: actions/github-script@v7
continue-on-error: true # Don't fail for fork PRs
with:
script: |
const pr = context.payload.pull_request;
@@ -50,6 +122,9 @@ jobs:
const deletions = pr.deletions;
const total = additions + deletions;
// Check if this is a fork PR
const isFork = pr.head.repo.full_name !== pr.base.repo.full_name;
let label = '';
let comment = '';
@@ -64,28 +139,39 @@ jobs:
comment = '🚨 This PR is **large** (>' + total + ' lines changed). Please consider breaking it into smaller, focused PRs for easier review.';
}
// Add size label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: [label]
});
// Only add labels/comments for non-fork PRs (fork PRs don't have write permission)
if (!isFork) {
try {
// Add size label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: [label]
});
// Add comment for large PRs
if (total >= 1000) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: comment
});
// Add comment for large PRs
if (total >= 1000) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: comment
});
}
} catch (error) {
console.log('Failed to add label/comment (expected for fork PRs):', error.message);
}
} else {
console.log('Fork PR detected - skipping label/comment (will be handled by pr-checks-comment.yml)');
}
# Backend tests
backend-tests:
name: Backend Tests (Go)
# Backend checks (simplified - no TA-Lib required)
backend-checks:
name: Backend Code Quality (Go)
runs-on: ubuntu-latest
permissions:
contents: read # Only need read access for testing
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -95,11 +181,6 @@ jobs:
with:
go-version: '1.21'
- name: Install TA-Lib
run: |
sudo apt-get update
sudo apt-get install -y libta-lib-dev
- name: Cache Go modules
uses: actions/cache@v4
with:
@@ -112,32 +193,32 @@ jobs:
run: go mod download
- name: Run go fmt
continue-on-error: true # Don't block PR if formatting issues found
run: |
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
echo "Please run 'go fmt' on your code"
echo "⚠️ Code formatting issues found. Please run 'go fmt ./...' locally."
echo ""
echo "Files needing formatting:"
gofmt -s -l .
echo ""
echo "This is a warning and won't block your PR from being merged."
exit 1
else
echo "✅ All Go files are properly formatted"
fi
- name: Run go vet
run: go vet ./...
- name: Run tests
run: go test -v -race -coverprofile=coverage.out ./...
- name: Build
run: go build -v -o nofx
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
flags: backend
# Frontend tests
frontend-tests:
name: Frontend Tests (React/TypeScript)
runs-on: ubuntu-latest
permissions:
contents: read # Only need read access for testing
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -159,24 +240,21 @@ jobs:
working-directory: ./web
run: npm ci
- name: Run linter
working-directory: ./web
run: npm run lint
- name: Run type check
working-directory: ./web
run: npm run type-check || true # Don't fail on type errors for now
- name: Build
- name: Build and Type Check
working-directory: ./web
run: npm run build
# Note: build script runs "tsc && vite build" which includes type checking
# Auto-label based on files changed
auto-label:
name: Auto Label PR
runs-on: ubuntu-latest
# Only run for non-fork PRs (fork PRs don't have write permission)
if: github.event.pull_request.head.repo.full_name == github.repository
permissions:
contents: read
pull-requests: write
issues: write # Required: PRs are issues, labeler needs to modify issue labels
steps:
- uses: actions/labeler@v5
with:
@@ -187,6 +265,9 @@ jobs:
security-check:
name: Security Scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # Required: Upload SARIF results to GitHub Security
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -209,29 +290,51 @@ jobs:
secrets-check:
name: Check for Secrets
runs-on: ubuntu-latest
permissions:
contents: read # Only need read access for scanning
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run TruffleHog OSS
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.pull_request.base.sha }}
head: ${{ github.event.pull_request.head.sha }}
extra_args: --debug --only-verified
# All checks passed
all-checks:
name: All Checks Passed
runs-on: ubuntu-latest
needs: [validate-pr, backend-tests, frontend-tests, security-check, secrets-check]
needs: [validate-pr, backend-checks, frontend-tests, security-check, secrets-check]
if: always()
permissions:
contents: read # Only need read access for status checking
steps:
- name: Check all jobs
run: |
if [ "${{ contains(needs.*.result, 'failure') }}" == "true" ]; then
echo "Some checks failed"
# Note: validate-pr uses continue-on-error, so it won't block even if title format is invalid
# We only care about actual test failures
echo "validate-pr: ${{ needs.validate-pr.result }}"
echo "backend-checks: ${{ needs.backend-checks.result }}"
echo "frontend-tests: ${{ needs.frontend-tests.result }}"
echo "security-check: ${{ needs.security-check.result }}"
echo "secrets-check: ${{ needs.secrets-check.result }}"
# Check if any critical checks failed (excluding validate-pr which is advisory)
if [[ "${{ needs.backend-checks.result }}" == "failure" ]] || \
[[ "${{ needs.frontend-tests.result }}" == "failure" ]] || \
[[ "${{ needs.security-check.result }}" == "failure" ]] || \
[[ "${{ needs.secrets-check.result }}" == "failure" ]]; then
echo "❌ Critical checks failed"
exit 1
else
echo "All checks passed!"
echo "✅ All critical checks passed!"
if [[ "${{ needs.validate-pr.result }}" != "success" ]]; then
echo " Note: PR title format check is advisory only and doesn't block merging"
fi
fi

674
LICENSE
View File

@@ -1,21 +1,661 @@
MIT License
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (c) 2025 Tinkle Community (NOFX Project)
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

View File

@@ -567,9 +567,9 @@ Open your browser and visit: **🌐 http://localhost:3000**
"ai_model": "deepseek",
"exchange": "aster",
"aster_user": "0x63DD5aCC6b1aa0f563956C0e534DD30B6dcF7C4e",
"aster_signer": "0x21cF8Ae13Bb72632562c6Fff438652Ba1a151bb0",
"aster_private_key": "4fd0a42218f3eae43a6ce26d22544e986139a01e5b34a62db53757ffca81bae1",
"aster_user": "0xYOUR_MAIN_WALLET_ADDRESS_HERE",
"aster_signer": "0xYOUR_API_WALLET_SIGNER_ADDRESS_HERE",
"aster_private_key": "your_api_wallet_private_key_without_0x_prefix",
"deepseek_key": "sk-xxxxxxxxxxxxx",
"initial_balance": 1000.0,
@@ -1170,6 +1170,8 @@ GET /api/health # Health check
## 🛠️ Common Issues
> 📖 **For detailed troubleshooting:** See the comprehensive [Troubleshooting Guide](docs/guides/TROUBLESHOOTING.md) ([中文版](docs/guides/TROUBLESHOOTING.zh-CN.md))
### 1. Compilation error: TA-Lib not found
**Solution**: Install TA-Lib library

View File

@@ -92,6 +92,10 @@ func (s *Server) setupRoutes() {
// 公开的竞赛数据(无需认证)
api.GET("/traders", s.handlePublicTraderList)
api.GET("/competition", s.handlePublicCompetition)
api.GET("/top-traders", s.handleTopTraders)
api.GET("/equity-history", s.handleEquityHistory)
api.GET("/equity-history-batch", s.handleEquityHistoryBatch)
api.GET("/traders/:id/public-config", s.handleGetPublicTraderConfig)
// 需要认证的路由
protected := api.Group("/", s.authMiddleware())
@@ -127,7 +131,6 @@ func (s *Server) setupRoutes() {
protected.GET("/decisions", s.handleDecisions)
protected.GET("/decisions/latest", s.handleLatestDecisions)
protected.GET("/statistics", s.handleStatistics)
protected.GET("/equity-history", s.handleEquityHistory)
protected.GET("/performance", s.handlePerformance)
}
}
@@ -513,8 +516,16 @@ func (s *Server) handleDeleteTrader(c *gin.Context) {
// handleStartTrader 启动交易员
func (s *Server) handleStartTrader(c *gin.Context) {
userID := c.GetString("user_id")
traderID := c.Param("id")
// 校验交易员是否属于当前用户
_, _, _, err := s.database.GetTraderConfig(userID, traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在或无访问权限"})
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"})
@@ -537,7 +548,6 @@ func (s *Server) handleStartTrader(c *gin.Context) {
}()
// 更新数据库中的运行状态
userID := c.GetString("user_id")
err = s.database.UpdateTraderStatus(userID, traderID, true)
if err != nil {
log.Printf("⚠️ 更新交易员状态失败: %v", err)
@@ -549,8 +559,16 @@ func (s *Server) handleStartTrader(c *gin.Context) {
// handleStopTrader 停止交易员
func (s *Server) handleStopTrader(c *gin.Context) {
userID := c.GetString("user_id")
traderID := c.Param("id")
// 校验交易员是否属于当前用户
_, _, _, err := s.database.GetTraderConfig(userID, traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在或无访问权限"})
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"})
@@ -568,7 +586,6 @@ func (s *Server) handleStopTrader(c *gin.Context) {
trader.Stop()
// 更新数据库中的运行状态
userID := c.GetString("user_id")
err = s.database.UpdateTraderStatus(userID, traderID, false)
if err != nil {
log.Printf("⚠️ 更新交易员状态失败: %v", err)
@@ -1441,7 +1458,12 @@ func (s *Server) Start() error {
log.Printf("🌐 API服务器启动在 http://localhost%s", addr)
log.Printf("📊 API文档:")
log.Printf(" • GET /api/health - 健康检查")
log.Printf(" • GET /api/traders - AI交易员列表")
log.Printf(" • GET /api/traders - 公开的AI交易员排行榜前50名无需认证")
log.Printf(" • GET /api/competition - 公开的竞赛数据(无需认证)")
log.Printf(" • GET /api/top-traders - 前10名交易员数据无需认证表现对比用")
log.Printf(" • GET /api/equity-history?trader_id=xxx - 公开的收益率历史数据(无需认证,竞赛用)")
log.Printf(" • GET /api/equity-history-batch?trader_ids=a,b,c - 批量获取历史数据(无需认证,表现对比优化)")
log.Printf(" • GET /api/traders/:id/public-config - 公开的交易员配置(无需认证,不含敏感信息)")
log.Printf(" • POST /api/traders - 创建新的AI交易员")
log.Printf(" • DELETE /api/traders/:id - 删除AI交易员")
log.Printf(" • POST /api/traders/:id/start - 启动AI交易员")
@@ -1456,7 +1478,6 @@ func (s *Server) Start() error {
log.Printf(" • GET /api/decisions?trader_id=xxx - 指定trader的决策日志")
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.Println()
@@ -1555,3 +1576,146 @@ func (s *Server) handlePublicCompetition(c *gin.Context) {
c.JSON(http.StatusOK, competition)
}
// handleTopTraders 获取前10名交易员数据无需认证用于表现对比
func (s *Server) handleTopTraders(c *gin.Context) {
topTraders, err := s.traderManager.GetTopTradersData()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("获取前10名交易员数据失败: %v", err),
})
return
}
c.JSON(http.StatusOK, topTraders)
}
// handleEquityHistoryBatch 批量获取多个交易员的收益率历史数据(无需认证,用于表现对比)
func (s *Server) handleEquityHistoryBatch(c *gin.Context) {
// 获取trader_ids参数支持逗号分隔的多个ID
traderIDsParam := c.Query("trader_ids")
if traderIDsParam == "" {
// 如果没有指定trader_ids则返回前10名的历史数据
topTraders, err := s.traderManager.GetTopTradersData()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf("获取前10名交易员失败: %v", err),
})
return
}
traders, ok := topTraders["traders"].([]map[string]interface{})
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"error": "交易员数据格式错误"})
return
}
// 提取trader IDs
traderIDs := make([]string, 0, len(traders))
for _, trader := range traders {
if traderID, ok := trader["trader_id"].(string); ok {
traderIDs = append(traderIDs, traderID)
}
}
result := s.getEquityHistoryForTraders(traderIDs)
c.JSON(http.StatusOK, result)
return
}
// 解析逗号分隔的trader IDs
traderIDs := strings.Split(traderIDsParam, ",")
for i := range traderIDs {
traderIDs[i] = strings.TrimSpace(traderIDs[i])
}
// 限制最多20个交易员防止请求过大
if len(traderIDs) > 20 {
traderIDs = traderIDs[:20]
}
result := s.getEquityHistoryForTraders(traderIDs)
c.JSON(http.StatusOK, result)
}
// getEquityHistoryForTraders 获取多个交易员的历史数据
func (s *Server) getEquityHistoryForTraders(traderIDs []string) map[string]interface{} {
result := make(map[string]interface{})
histories := make(map[string]interface{})
errors := make(map[string]string)
for _, traderID := range traderIDs {
if traderID == "" {
continue
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
errors[traderID] = "交易员不存在"
continue
}
// 获取历史数据(用于对比展示,限制数据量)
records, err := trader.GetDecisionLogger().GetLatestRecords(500)
if err != nil {
errors[traderID] = fmt.Sprintf("获取历史数据失败: %v", err)
continue
}
// 构建收益率历史数据
history := make([]map[string]interface{}, 0, len(records))
for _, record := range records {
// 计算总权益(余额+未实现盈亏)
totalEquity := record.AccountState.TotalBalance + record.AccountState.TotalUnrealizedProfit
history = append(history, map[string]interface{}{
"timestamp": record.Timestamp,
"total_equity": totalEquity,
"total_pnl": record.AccountState.TotalUnrealizedProfit,
"balance": record.AccountState.TotalBalance,
})
}
histories[traderID] = history
}
result["histories"] = histories
result["count"] = len(histories)
if len(errors) > 0 {
result["errors"] = errors
}
return result
}
// handleGetPublicTraderConfig 获取公开的交易员配置信息(无需认证,不包含敏感信息)
func (s *Server) handleGetPublicTraderConfig(c *gin.Context) {
traderID := c.Param("id")
if traderID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "交易员ID不能为空"})
return
}
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"})
return
}
// 获取交易员的状态信息
status := trader.GetStatus()
// 只返回公开的配置信息不包含API密钥等敏感数据
result := map[string]interface{}{
"trader_id": trader.GetID(),
"trader_name": trader.GetName(),
"ai_model": trader.GetAIModel(),
"exchange": trader.GetExchange(),
"is_running": status["is_running"],
"ai_provider": status["ai_provider"],
"start_time": status["start_time"],
}
c.JSON(http.StatusOK, result)
}

View File

@@ -47,7 +47,9 @@ COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=1 GOOS=linux go build -trimpath -ldflags="-s -w" -o nofx .
RUN CGO_ENABLED=1 GOOS=linux \
CGO_CFLAGS="-D_LARGEFILE64_SOURCE" \
go build -trimpath -ldflags="-s -w" -o nofx .
# ──────────────────────────────────────────────────────────────
# Runtime Stage (Minimal Executable Environment)

View File

@@ -0,0 +1,612 @@
# 🔧 Troubleshooting Guide
This guide helps you diagnose and fix common issues before submitting a bug report.
---
## 📋 Quick Diagnostic Checklist
Before reporting a bug, please check:
1.**Backend is running**: `docker compose ps` or `ps aux | grep nofx`
2.**Frontend is accessible**: Open http://localhost:3000 in browser
3.**API is responding**: `curl http://localhost:8080/api/health`
4.**Check logs for errors**: See [How to Capture Logs](#how-to-capture-logs) below
---
## 🐛 Common Issues & Solutions
### 1. Trading Issues
#### ❌ Only Opening Short Positions (Issue #202)
**Symptom:** AI only opens short positions, never long positions, even when market is bullish.
**Root Cause:** Binance account is in **One-way Mode** instead of **Hedge Mode**.
**Solution:**
1. Login to [Binance Futures](https://www.binance.com/futures/BTCUSDT)
2. Click **⚙️ Preferences** (top right)
3. Select **Position Mode**
4. Switch to **Hedge Mode** (双向持仓)
5. ⚠️ **Important:** Close all positions before switching
**Why this happens:**
- Code uses `PositionSide(LONG)` and `PositionSide(SHORT)` parameters
- These only work in Hedge Mode
- In One-way Mode, orders fail or only one direction works
**For Subaccounts:**
- Some Binance subaccounts may not have permission to change position mode
- Use main account or contact Binance support to enable this permission
---
#### ❌ Order Error: `code=-4061` Position Side Mismatch
**Error Message:** `Order's position side does not match user's setting`
**Solution:** Same as above - switch to Hedge Mode.
---
#### ❌ Leverage Error: `Subaccounts restricted to 5x leverage`
**Symptom:** Orders fail with leverage error when trying to use >5x leverage.
**Solution:**
1. Open Web UI → Trader Settings
2. Set leverage to 5x or lower:
```json
{
"btc_eth_leverage": 5,
"altcoin_leverage": 5
}
```
3. Or use main account (supports up to 50x BTC/ETH, 20x altcoins)
---
#### ❌ Positions Not Executing
**Check these:**
1. **API Permissions**:
- Go to Binance → API Management
- Verify "Enable Futures" is checked
- Check IP whitelist (if enabled)
2. **Account Balance**:
- Ensure sufficient USDT in Futures wallet
- Check margin usage is not at 100%
3. **Symbol Status**:
- Verify trading pair is active on exchange
- Check if symbol is in maintenance mode
4. **Decision Logs**:
```bash
# Check latest decision
ls -lt decision_logs/your_trader_id/ | head -5
cat decision_logs/your_trader_id/latest_file.json
```
- Look for AI decision: was it "wait", "hold", or actual trade?
- Check if position_size_usd is within limits
---
### 2. AI Decision Issues
#### ❌ AI Always Says "Wait" / "Hold"
**Possible Causes:**
1. **Market Conditions**: AI may genuinely see no good opportunities
2. **Risk Limits**: Account equity too low, margin usage too high
3. **Historical Performance**: AI being cautious after losses
**How to Check:**
```bash
# View latest decision reasoning
cat decision_logs/your_trader_id/$(ls -t decision_logs/your_trader_id/ | head -1)
```
Look at the AI's Chain-of-Thought reasoning section.
**Solutions:**
- Wait for better market conditions
- Check if all candidate coins have low liquidity
- Verify `use_default_coins: true` or coin pool API is working
---
#### ❌ AI Making Bad Decisions
**Remember:** AI trading is experimental and not guaranteed to be profitable.
**Things to Check:**
1. **Decision Interval**: Is it too short? (Recommended: 3-5 minutes)
2. **Leverage Settings**: Too aggressive?
3. **Historical Feedback**: Check performance logs to see if AI is learning
4. **Market Volatility**: High volatility = higher risk
**Adjustments:**
- Reduce leverage for more conservative trading
- Increase decision interval to reduce over-trading
- Use smaller initial balance for testing
---
### 3. Connection & API Issues
#### ❌ Docker Image Pull Failed (China Mainland)
**Error:** `ERROR [internal] load metadata for docker.io/library/...`
**Symptoms:**
- `docker compose build` or `docker compose up` hangs
- Timeout errors: `timeout`, `connection refused`
- Cannot pull images from Docker Hub
**Root Cause:**
Access to Docker Hub is restricted or extremely slow in mainland China.
**Solution 1: Configure Docker Registry Mirror (Recommended)**
1. **Edit Docker configuration file:**
```bash
# Linux
sudo nano /etc/docker/daemon.json
# macOS (Docker Desktop)
# Settings → Docker Engine
```
2. **Add China registry mirrors:**
```json
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.1panel.live",
"https://hub.rat.dev",
"https://dockerpull.com",
"https://dockerhub.icu"
]
}
```
3. **Restart Docker:**
```bash
# Linux
sudo systemctl restart docker
# macOS/Windows
# Restart Docker Desktop
```
4. **Rebuild:**
```bash
docker compose build --no-cache
docker compose up -d
```
**Solution 2: Use VPN**
1. Connect to VPN (Taiwan nodes recommended)
2. Ensure **global mode** instead of rule-based mode
3. Re-run `docker compose build`
**Solution 3: Offline Image Download**
If above methods don't work:
1. **Use image proxy websites:**
- https://proxy.vvvv.ee/images.html (offline download available)
- https://github.com/dongyubin/DockerHub (mirror list)
2. **Manually import images:**
```bash
# After downloading image files
docker load -i golang-1.25-alpine.tar
docker load -i node-20-alpine.tar
docker load -i nginx-alpine.tar
```
3. **Verify images are loaded:**
```bash
docker images | grep golang
docker images | grep node
docker images | grep nginx
```
**Verify registry mirror is working:**
```bash
# Check Docker info
docker info | grep -A 10 "Registry Mirrors"
# Should show your configured mirrors
```
**Related Issue:** [#168](https://github.com/tinkle-community/nofx/issues/168)
---
#### ❌ Backend Won't Start
**Error:** `port 8080 already in use`
**Solution:**
```bash
# Find what's using the port
lsof -i :8080
# OR
netstat -tulpn | grep 8080
# Kill the process or change port in .env
NOFX_BACKEND_PORT=8081
```
---
#### ❌ Frontend Can't Connect to Backend
**Symptoms:**
- UI shows "Loading..." forever
- Browser console shows 404 or network errors
**Solutions:**
1. **Check backend is running:**
```bash
docker compose ps # Should show backend as "Up"
# OR
curl http://localhost:8080/api/health # Should return {"status":"ok"}
```
2. **Check port configuration:**
- Backend default: 8080
- Frontend default: 3000
- Verify `.env` settings match
3. **CORS Issues:**
- If running frontend and backend on different ports/domains
- Check browser console for CORS errors
- Backend should allow frontend origin
---
#### ❌ Exchange API Errors
**Common Errors:**
- `code=-1021, msg=Timestamp for this request is outside of the recvWindow`
- `invalid signature`
- `timestamp` errors
**Root Cause:**
System time is inaccurate, differing from Binance server time by more than allowed range (typically 5 seconds).
**Solution 1: Sync System Time (Recommended)**
```bash
# Method 1: Use ntpdate (most common)
sudo ntpdate pool.ntp.org
# Method 2: Use other NTP servers
sudo ntpdate -s time.nist.gov
sudo ntpdate -s ntp.aliyun.com # Aliyun NTP (fast in China)
# Method 3: Enable automatic time sync (Linux)
sudo timedatectl set-ntp true
# Verify time is correct
date
# Should show current accurate time
```
**Docker Environment Special Note:**
If using Docker, container time may be out of sync with host:
```bash
# Check container time
docker exec nofx-backend date
# If time is wrong, restart Docker service
sudo systemctl restart docker
# Or add timezone in docker-compose.yml
environment:
- TZ=Asia/Shanghai # or your timezone
```
**Solution 2: Verify API Keys**
If errors persist after time sync:
1. **Check API Keys:**
- Not expired
- Have correct permissions (Futures enabled)
- IP whitelist includes your server IP
2. **Regenerate API Keys:**
- Login to Binance → API Management
- Delete old key
- Create new key
- Update NOFX configuration
**Solution 3: Check Rate Limits**
Binance has strict API rate limits:
- **Requests per minute limit**
- Reduce number of traders
- Increase decision interval (e.g., from 1min to 3-5min)
**Related Issue:** [#60](https://github.com/tinkle-community/nofx/issues/60)
---
### 4. Frontend Issues
#### ❌ UI Not Updating / Showing Old Data
**Solutions:**
1. **Hard Refresh:**
- Chrome/Firefox: `Ctrl+Shift+R` (Windows/Linux) or `Cmd+Shift+R` (Mac)
- Safari: `Cmd+Option+R`
2. **Clear Browser Cache:**
- Settings → Privacy → Clear browsing data
- Or open in Incognito/Private mode
3. **Check SWR Polling:**
- Frontend uses SWR with 5-10s intervals
- Data should auto-refresh
- Check browser console for fetch errors
---
#### ❌ Charts Not Rendering
**Possible Causes:**
1. No historical data yet (system just started)
2. JavaScript errors in console
3. Browser compatibility issues
**Solutions:**
- Wait 5-10 minutes for data to accumulate
- Check browser console (F12) for errors
- Try different browser (Chrome recommended)
- Ensure backend API endpoints are returning data
---
### 5. Database Issues
#### ❌ `database is locked` Error
**Cause:** SQLite database being accessed by multiple processes.
**Solution:**
```bash
# Stop all NOFX processes
docker compose down
# OR
pkill nofx
# Restart
docker compose up -d
# OR
./nofx
```
---
#### ❌ Trader Configuration Not Saving
**Check:**
1. **Permissions:**
```bash
ls -l config.db trading.db
# Should be writable by current user
```
2. **Disk Space:**
```bash
df -h # Ensure disk not full
```
3. **Database Integrity:**
```bash
sqlite3 config.db "PRAGMA integrity_check;"
```
---
## 📊 How to Capture Logs
### Backend Logs
**Docker:**
```bash
# View last 100 lines
docker compose logs backend --tail=100
# Follow live logs
docker compose logs -f backend
# Save to file
docker compose logs backend --tail=500 > backend_logs.txt
```
**Manual/PM2:**
```bash
# Terminal where you ran ./nofx shows logs
# PM2:
pm2 logs nofx --lines 100
# Save to file
pm2 logs nofx --lines 500 > backend_logs.txt
```
---
### Frontend Logs (Browser Console)
1. **Open DevTools:**
- Press `F12` or Right-click → Inspect
2. **Console Tab:**
- See JavaScript errors and warnings
- Look for red error messages
3. **Network Tab:**
- Filter by "XHR" or "Fetch"
- Look for failed requests (red status codes)
- Click on failed request → Preview/Response to see error details
4. **Capture Screenshot:**
- Windows: `Win+Shift+S`
- Mac: `Cmd+Shift+4`
- Or use browser DevTools screenshot feature
---
### Decision Logs (Trading Issues)
```bash
# List recent decision logs
ls -lt decision_logs/your_trader_id/ | head -10
# View latest decision
cat decision_logs/your_trader_id/$(ls -t decision_logs/your_trader_id/ | head -1) | jq .
# Search for specific symbol
grep -r "BTCUSDT" decision_logs/your_trader_id/
# Find decisions that resulted in trades
grep -r '"action": "open_' decision_logs/your_trader_id/
```
**What to look for in decision logs:**
- `chain_of_thought`: AI's reasoning process
- `user_prompt`: Market data AI received
- `decision`: Final decision (action, symbol, leverage, etc.)
- `account_state`: Account balance, margin, positions at decision time
- `execution_result`: Whether trade succeeded or failed
---
## 🔍 Diagnostic Commands
### System Health Check
```bash
# Backend health
curl http://localhost:8080/api/health
# List all traders
curl http://localhost:8080/api/traders
# Check specific trader status
curl http://localhost:8080/api/status?trader_id=your_trader_id
# Get account info
curl http://localhost:8080/api/account?trader_id=your_trader_id
```
### Docker Status
```bash
# Check all containers
docker compose ps
# Check resource usage
docker stats
# Restart specific service
docker compose restart backend
docker compose restart frontend
```
### Database Queries
```bash
# Check traders in database
sqlite3 config.db "SELECT id, name, ai_model_id, exchange_id, is_running FROM traders;"
# Check AI models
sqlite3 config.db "SELECT id, name, model_type, enabled FROM ai_models;"
# Check system config
sqlite3 config.db "SELECT key, value FROM system_config;"
```
---
## 📝 Still Having Issues?
If you've tried all the above and still have problems:
1. **Gather Information:**
- Backend logs (last 100 lines)
- Frontend console screenshot
- Decision logs (if trading issue)
- Your environment details
2. **Submit Bug Report:**
- Use the [Bug Report Template](../../.github/ISSUE_TEMPLATE/bug_report.md)
- Include all logs and screenshots
- Describe what you've already tried
3. **Join Community:**
- [Telegram Developer Community](https://t.me/nofx_dev_community)
- [GitHub Discussions](https://github.com/tinkle-community/nofx/discussions)
---
## 🆘 Emergency: System Completely Broken
**Complete Reset (⚠️ Will lose trading history):**
```bash
# Stop everything
docker compose down
# Backup databases (just in case)
cp config.db config.db.backup
cp trading.db trading.db.backup
# Remove databases (fresh start)
rm config.db trading.db
# Restart
docker compose up -d --build
# Reconfigure through web UI
open http://localhost:3000
```
**Partial Reset (Keep configuration, clear logs):**
```bash
# Clear decision logs
rm -rf decision_logs/*
# Clear Docker cache and rebuild
docker compose down
docker compose build --no-cache
docker compose up -d
```
---
## 📚 Additional Resources
- **[FAQ](faq.en.md)** - Frequently Asked Questions
- **[Getting Started](../getting-started/README.md)** - Setup guide
- **[Architecture Docs](../architecture/README.md)** - How the system works
- **[CLAUDE.md](../../CLAUDE.md)** - Developer documentation
---
**Last Updated:** 2025-11-02

View File

@@ -0,0 +1,612 @@
# 🔧 故障排查指南
本指南帮助您在提交 bug 报告前自行诊断和修复常见问题。
---
## 📋 快速诊断清单
提交 bug 前,请检查:
1.**后端正在运行**: `docker compose ps``ps aux | grep nofx`
2.**前端可访问**: 在浏览器打开 http://localhost:3000
3.**API 正常响应**: `curl http://localhost:8080/api/health`
4.**检查日志中的错误**: 参见下方 [如何捕获日志](#如何捕获日志)
---
## 🐛 常见问题与解决方案
### 1. 交易问题
#### ❌ 只开空单,不开多单 (Issue #202)
**症状:** AI 只开空仓,从不开多仓,即使市场看涨。
**根本原因:** 币安账户处于**单向持仓模式**而非**双向持仓模式**。
**解决方案:**
1. 登录 [币安合约交易](https://www.binance.com/zh-CN/futures/BTCUSDT)
2. 点击右上角 **⚙️ 偏好设置**
3. 选择 **持仓模式**
4. 切换为 **双向持仓** (Hedge Mode)
5. ⚠️ **重要:** 切换前必须先平掉所有持仓
**为什么会这样:**
- 代码使用 `PositionSide(LONG)``PositionSide(SHORT)` 参数
- 这些参数只在双向持仓模式下有效
- 在单向持仓模式下,订单会失败或只有一个方向有效
**关于子账户:**
- 部分币安子账户可能没有权限更改持仓模式
- 使用主账户或联系币安客服开通此权限
---
#### ❌ 订单错误: `code=-4061` 持仓方向不匹配
**错误信息:** `Order's position side does not match user's setting`
**解决方案:** 同上 - 切换到双向持仓模式。
---
#### ❌ 杠杆错误: `子账户限制最高5倍杠杆`
**症状:** 尝试使用 >5倍杠杆时订单失败。
**解决方案:**
1. 打开 Web 界面 → 交易员设置
2. 将杠杆设置为 5倍或更低:
```json
{
"btc_eth_leverage": 5,
"altcoin_leverage": 5
}
```
3. 或使用主账户(支持最高 50倍 BTC/ETH20倍山寨币
---
#### ❌ 持仓无法执行
**检查以下内容:**
1. **API 权限**:
- 进入币安 → API 管理
- 确认"启用合约"已勾选
- 检查 IP 白名单(如果启用)
2. **账户余额**:
- 确保合约钱包中有足够的 USDT
- 检查保证金使用率未达到 100%
3. **交易对状态**:
- 确认交易对在交易所处于活跃状态
- 检查交易对是否处于维护模式
4. **决策日志**:
```bash
# 检查最新决策
ls -lt decision_logs/your_trader_id/ | head -5
cat decision_logs/your_trader_id/latest_file.json
```
- 查看 AI 决策:是"wait"、"hold"还是实际交易?
- 检查 position_size_usd 是否在限制范围内
---
### 2. AI 决策问题
#### ❌ AI 总是说"等待"/"持有"
**可能原因:**
1. **市场情况**: AI 可能确实没看到好的机会
2. **风险限制**: 账户净值太低、保证金使用率太高
3. **历史表现**: AI 在亏损后变得谨慎
**如何检查:**
```bash
# 查看最新决策推理
cat decision_logs/your_trader_id/$(ls -t decision_logs/your_trader_id/ | head -1)
```
查看 AI 的思维链Chain-of-Thought推理部分。
**解决方案:**
- 等待更好的市场条件
- 检查候选币种是否流动性都太低
- 确认 `use_default_coins: true` 或币种池 API 正常工作
---
#### ❌ AI 做出错误决策
**请记住:** AI 交易是实验性的,不保证盈利。
**需要检查的事项:**
1. **决策间隔**: 是否太短?(推荐: 3-5分钟
2. **杠杆设置**: 是否过于激进?
3. **历史反馈**: 查看表现日志,看 AI 是否在学习
4. **市场波动**: 高波动 = 更高风险
**调整建议:**
- 降低杠杆以实现更保守的交易
- 增加决策间隔以减少过度交易
- 使用较小的初始余额进行测试
---
### 3. 连接和 API 问题
#### ❌ Docker 镜像下载失败 (中国大陆)
**错误:** `ERROR [internal] load metadata for docker.io/library/...`
**症状:**
- `docker compose build` 或 `docker compose up` 卡住
- 超时错误: `timeout`、`connection refused`
- 无法从 Docker Hub 拉取镜像
**根本原因:**
中国大陆访问 Docker Hub 受限或速度极慢。
**解决方案 1: 配置 Docker 镜像加速器(推荐)**
1. **编辑 Docker 配置文件:**
```bash
# Linux
sudo nano /etc/docker/daemon.json
# macOS (Docker Desktop)
# Settings → Docker Engine
```
2. **添加国内镜像源:**
```json
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.1panel.live",
"https://hub.rat.dev",
"https://dockerpull.com",
"https://dockerhub.icu"
]
}
```
3. **重启 Docker:**
```bash
# Linux
sudo systemctl restart docker
# macOS/Windows
# 重启 Docker Desktop
```
4. **重新构建:**
```bash
docker compose build --no-cache
docker compose up -d
```
**解决方案 2: 使用 VPN**
1. 连接 VPN推荐台湾节点
2. 确保使用**全局模式**而非规则模式
3. 重新运行 `docker compose build`
**解决方案 3: 离线下载镜像**
如果上述方法都不行:
1. **使用镜像代理网站下载:**
- https://proxy.vvvv.ee/images.html (可离线下载)
- https://github.com/dongyubin/DockerHub (镜像加速列表)
2. **手动导入镜像:**
```bash
# 下载镜像文件后
docker load -i golang-1.25-alpine.tar
docker load -i node-20-alpine.tar
docker load -i nginx-alpine.tar
```
3. **验证镜像已加载:**
```bash
docker images | grep golang
docker images | grep node
docker images | grep nginx
```
**验证镜像加速器是否生效:**
```bash
# 查看 Docker 信息
docker info | grep -A 10 "Registry Mirrors"
# 应该显示你配置的镜像源
```
**相关 Issue:** [#168](https://github.com/tinkle-community/nofx/issues/168)
---
#### ❌ 后端无法启动
**错误:** `port 8080 already in use`
**解决方案:**
```bash
# 查找占用端口的进程
lsof -i :8080
# 或
netstat -tulpn | grep 8080
# 杀死进程或在 .env 中更改端口
NOFX_BACKEND_PORT=8081
```
---
#### ❌ 前端无法连接后端
**症状:**
- UI 显示"加载中..."一直不结束
- 浏览器控制台显示 404 或网络错误
**解决方案:**
1. **检查后端是否运行:**
```bash
docker compose ps # 应显示 backend 为 "Up"
# 或
curl http://localhost:8080/api/health # 应返回 {"status":"ok"}
```
2. **检查端口配置:**
- 后端默认: 8080
- 前端默认: 3000
- 确认 `.env` 设置匹配
3. **CORS 问题:**
- 如果前端和后端运行在不同端口/域名
- 检查浏览器控制台的 CORS 错误
- 后端应允许前端来源
---
#### ❌ 交易所 API 错误
**常见错误:**
- `code=-1021, msg=Timestamp for this request is outside of the recvWindow`
- `invalid signature`
- `timestamp` 错误
**根本原因:**
系统时间不准确,与币安服务器时间相差超过允许范围(通常是 5 秒)。
**解决方案 1: 同步系统时间(推荐)**
```bash
# 方法 1: 使用 ntpdate (最常用)
sudo ntpdate pool.ntp.org
# 方法 2: 使用其他 NTP 服务器
sudo ntpdate -s time.nist.gov
sudo ntpdate -s ntp.aliyun.com # 阿里云 NTP (中国大陆快)
# 方法 3: 启用自动时间同步 (Linux)
sudo timedatectl set-ntp true
# 验证时间是否正确
date
# 应该显示正确的当前时间
```
**Docker 环境特别注意:**
如果使用 Docker容器时间可能与宿主机不同步
```bash
# 检查容器时间
docker exec nofx-backend date
# 如果时间错误,重启 Docker 服务
sudo systemctl restart docker
# 或在 docker-compose.yml 中添加时区设置
environment:
- TZ=Asia/Shanghai # 或您的时区
```
**解决方案 2: 验证 API 密钥**
如果时间同步后仍有错误:
1. **检查 API 密钥:**
- 未过期
- 有正确权限(已启用合约)
- IP 白名单包含您的服务器 IP
2. **重新生成 API 密钥:**
- 登录币安 → API 管理
- 删除旧密钥
- 创建新密钥
- 更新 NOFX 配置
**解决方案 3: 检查速率限制**
币安有严格的 API 速率限制:
- **每分钟请求数限制**
- 减少交易员数量
- 增加决策间隔时间(例如从 1 分钟改为 3-5 分钟)
**相关 Issue:** [#60](https://github.com/tinkle-community/nofx/issues/60)
---
### 4. 前端问题
#### ❌ UI 不更新 / 显示旧数据
**解决方案:**
1. **强制刷新:**
- Chrome/Firefox: `Ctrl+Shift+R` (Windows/Linux) 或 `Cmd+Shift+R` (Mac)
- Safari: `Cmd+Option+R`
2. **清除浏览器缓存:**
- 设置 → 隐私 → 清除浏览数据
- 或在无痕/隐私模式下打开
3. **检查 SWR 轮询:**
- 前端使用 5-10秒间隔的 SWR
- 数据应自动刷新
- 检查浏览器控制台是否有 fetch 错误
---
#### ❌ 图表不渲染
**可能原因:**
1. 暂无历史数据(系统刚启动)
2. 控制台中有 JavaScript 错误
3. 浏览器兼容性问题
**解决方案:**
- 等待 5-10 分钟让数据积累
- 检查浏览器控制台F12是否有错误
- 尝试不同浏览器(推荐 Chrome
- 确保后端 API 端点正在返回数据
---
### 5. 数据库问题
#### ❌ `database is locked` 错误
**原因:** SQLite 数据库被多个进程访问。
**解决方案:**
```bash
# 停止所有 NOFX 进程
docker compose down
# 或
pkill nofx
# 重启
docker compose up -d
# 或
./nofx
```
---
#### ❌ 交易员配置无法保存
**检查:**
1. **权限:**
```bash
ls -l config.db trading.db
# 应该对当前用户可写
```
2. **磁盘空间:**
```bash
df -h # 确保磁盘未满
```
3. **数据库完整性:**
```bash
sqlite3 config.db "PRAGMA integrity_check;"
```
---
## 📊 如何捕获日志
### 后端日志
**Docker:**
```bash
# 查看最后 100 行
docker compose logs backend --tail=100
# 实时跟踪日志
docker compose logs -f backend
# 保存到文件
docker compose logs backend --tail=500 > backend_logs.txt
```
**手动/PM2:**
```bash
# 运行 ./nofx 的终端会显示日志
# PM2:
pm2 logs nofx --lines 100
# 保存到文件
pm2 logs nofx --lines 500 > backend_logs.txt
```
---
### 前端日志(浏览器控制台)
1. **打开开发者工具:**
- 按 `F12` 或右键 → 检查
2. **Console控制台标签:**
- 查看 JavaScript 错误和警告
- 寻找红色错误消息
3. **Network网络标签:**
- 按"XHR"或"Fetch"筛选
- 查找失败的请求(红色状态码)
- 点击失败的请求 → Preview/Response 查看错误详情
4. **捕获截图:**
- Windows: `Win+Shift+S`
- Mac: `Cmd+Shift+4`
- 或使用浏览器开发者工具截图功能
---
### 决策日志(交易问题)
```bash
# 列出最近的决策日志
ls -lt decision_logs/your_trader_id/ | head -10
# 查看最新决策
cat decision_logs/your_trader_id/$(ls -t decision_logs/your_trader_id/ | head -1) | jq .
# 搜索特定交易对
grep -r "BTCUSDT" decision_logs/your_trader_id/
# 查找执行交易的决策
grep -r '"action": "open_' decision_logs/your_trader_id/
```
**决策日志中要查看的内容:**
- `chain_of_thought`: AI 的推理过程
- `user_prompt`: AI 收到的市场数据
- `decision`: 最终决策(动作、交易对、杠杆等)
- `account_state`: 决策时的账户余额、保证金、持仓
- `execution_result`: 交易是否成功
---
## 🔍 诊断命令
### 系统健康检查
```bash
# 后端健康状态
curl http://localhost:8080/api/health
# 列出所有交易员
curl http://localhost:8080/api/traders
# 检查特定交易员状态
curl http://localhost:8080/api/status?trader_id=your_trader_id
# 获取账户信息
curl http://localhost:8080/api/account?trader_id=your_trader_id
```
### Docker 状态
```bash
# 检查所有容器
docker compose ps
# 检查资源使用
docker stats
# 重启特定服务
docker compose restart backend
docker compose restart frontend
```
### 数据库查询
```bash
# 检查数据库中的交易员
sqlite3 config.db "SELECT id, name, ai_model_id, exchange_id, is_running FROM traders;"
# 检查 AI 模型
sqlite3 config.db "SELECT id, name, model_type, enabled FROM ai_models;"
# 检查系统配置
sqlite3 config.db "SELECT key, value FROM system_config;"
```
---
## 📝 仍有问题?
如果尝试了上述所有方法仍有问题:
1. **收集信息:**
- 后端日志(最后 100 行)
- 前端控制台截图
- 决策日志(如果是交易问题)
- 您的环境详情
2. **提交 Bug 报告:**
- 使用 [Bug 报告模板](../../.github/ISSUE_TEMPLATE/bug_report.md)
- 包含所有日志和截图
- 描述您已尝试的方法
3. **加入社区:**
- [Telegram 开发者社区](https://t.me/nofx_dev_community)
- [GitHub Discussions](https://github.com/tinkle-community/nofx/discussions)
---
## 🆘 紧急情况:系统完全损坏
**完全重置 (⚠️ 将丢失交易历史):**
```bash
# 停止所有服务
docker compose down
# 备份数据库(以防万一)
cp config.db config.db.backup
cp trading.db trading.db.backup
# 删除数据库(全新开始)
rm config.db trading.db
# 重启
docker compose up -d --build
# 通过 Web UI 重新配置
open http://localhost:3000
```
**部分重置(保留配置,清除日志):**
```bash
# 清除决策日志
rm -rf decision_logs/*
# 清除 Docker 缓存并重建
docker compose down
docker compose build --no-cache
docker compose up -d
```
---
## 📚 其他资源
- **[FAQ](faq.zh-CN.md)** - 常见问题
- **[快速开始](../getting-started/README.zh-CN.md)** - 安装指南
- **[架构文档](../architecture/README.zh-CN.md)** - 系统工作原理
- **[CLAUDE.md](../../CLAUDE.md)** - 开发者文档
---
**最后更新:** 2025-11-02

View File

@@ -1,25 +1,206 @@
# Frequently Asked Questions
# Frequently Asked Questions (FAQ)
## Binance Position Mode Error (code=-4061)
**Error Message**: `Order's position side does not match user's setting`
**Cause**: The system requires Hedge Mode (dual position), but your Binance account is set to One-way Mode.
### Solution
1. Login to [Binance Futures Trading Platform](https://www.binance.com/en/futures/BTCUSDT)
2. Click **⚙️ Preferences** in the top right corner
3. Select **Position Mode**
4. Switch to **Hedge Mode** (Dual Position)
5. Confirm the change
**Note**: You must close all open positions before switching modes.
Quick answers to common questions. For detailed troubleshooting, see [Troubleshooting Guide](TROUBLESHOOTING.md).
---
For more issues, check [GitHub Issues](https://github.com/tinkle-community/nofx/issues)
## General Questions
### What is NOFX?
NOFX is an AI-powered cryptocurrency trading bot that uses large language models (LLMs) to make trading decisions on futures markets.
### Which exchanges are supported?
- ✅ Binance Futures
- ✅ Hyperliquid
- 🚧 More exchanges coming soon
### Is NOFX profitable?
AI trading is **experimental** and **not guaranteed** to be profitable. Always start with small amounts and never invest more than you can afford to lose.
### Can I run multiple traders simultaneously?
Yes! NOFX supports running multiple traders with different configurations, AI models, and trading strategies.
---
## Setup & Configuration
### What are the system requirements?
- **OS**: Linux, macOS, or Windows (Docker recommended)
- **RAM**: 2GB minimum, 4GB recommended
- **Disk**: 1GB for application + logs
- **Network**: Stable internet connection
### Do I need coding experience?
No! NOFX has a web UI for all configuration. However, basic command line knowledge helps with setup and troubleshooting.
### How do I get API keys?
1. **Binance**: Account → API Management → Create API → Enable Futures
2. **Hyperliquid**: Visit [Hyperliquid App](https://app.hyperliquid.xyz/) → API Settings
### Should I use a subaccount?
**Recommended**: Yes, use a subaccount dedicated to NOFX for better risk isolation. However, note that some subaccounts have restrictions (e.g., 5x max leverage on Binance).
---
## Trading Questions
### Why isn't my trader making any trades?
Common reasons:
- AI decided to "wait" due to market conditions
- Insufficient balance or margin
- Position limits reached (default: max 3 positions)
- See detailed diagnostics in [Troubleshooting Guide](TROUBLESHOOTING.md#-ai-always-says-wait--hold)
### How often does the AI make decisions?
Configurable! Default is every **3-5 minutes**. Too frequent = overtrading, too slow = missed opportunities.
### Can I customize the trading strategy?
Yes! You can:
- Adjust leverage settings
- Modify coin selection pool
- Change decision intervals
- Customize system prompts (advanced)
### What's the maximum number of concurrent positions?
Default: **3 positions**. This is a soft limit defined in the AI prompt, not hard-coded. See `decision/engine.go:266`.
---
## Technical Issues
### Binance Position Mode Error (code=-4061)
**Error**: `Order's position side does not match user's setting`
**Solution**: Switch to **Hedge Mode** (双向持仓)
1. Login to [Binance Futures](https://www.binance.com/en/futures/BTCUSDT)
2. Click **⚙️ Preferences** (top right)
3. Select **Position Mode****Hedge Mode**
4. ⚠️ Close all positions first
**Why**: NOFX uses `PositionSide(LONG/SHORT)` which requires Hedge Mode.
See [Issue #202](https://github.com/tinkle-community/nofx/issues/202) and [Troubleshooting Guide](TROUBLESHOOTING.md#-only-opening-short-positions-issue-202).
---
### Backend won't start / Port already in use
**Solution**:
```bash
# Check what's using port 8080
lsof -i :8080
# Change port in .env
NOFX_BACKEND_PORT=8081
```
---
### Frontend shows "Loading..." forever
**Quick Check**:
```bash
# Is backend running?
curl http://localhost:8080/api/health
# Should return: {"status":"ok"}
```
If not, check [Troubleshooting Guide](TROUBLESHOOTING.md#-frontend-cant-connect-to-backend).
---
### Database locked error
**Solution**:
```bash
# Stop all NOFX processes
docker compose down
# OR
pkill nofx
# Restart
docker compose up -d
```
---
## AI & Model Questions
### Which AI models are supported?
- **DeepSeek** (recommended for cost/performance)
- **Qwen** (Alibaba Cloud Tongyi Qianwen)
- **Custom OpenAI-compatible APIs** (can be used for OpenAI, Claude via proxy, or other providers)
### How much do API calls cost?
Depends on your model and decision frequency:
- **DeepSeek**: ~$0.10-0.50 per day (1 trader, 5min intervals)
- **Qwen**: ~$0.20-0.80 per day
- **Custom API** (e.g., OpenAI GPT-4): ~$2-5 per day
*Estimates based on typical usage. Actual costs vary by provider and usage.*
### Can I use multiple AI models?
Yes! Each trader can use a different AI model. You can even A/B test different models.
### Does the AI learn from its mistakes?
Yes, to some extent. NOFX provides historical performance feedback in each decision prompt, allowing the AI to adjust its strategy.
---
## Data & Privacy
### Where is my data stored?
All data is stored **locally** on your machine in SQLite databases:
- `config.db` - Trader configurations
- `trading.db` - Trade history
- `decision_logs/` - AI decision records
### Is my API key secure?
API keys are stored in local databases. Never share your databases or `.env` files. We recommend using API keys with IP whitelist restrictions.
### Can I export my trading history?
Yes! Trading data is in SQLite format. You can query it directly:
```bash
sqlite3 trading.db "SELECT * FROM trades;"
```
---
## Troubleshooting
### Where can I find detailed troubleshooting?
See the comprehensive [Troubleshooting Guide](TROUBLESHOOTING.md) for:
- Step-by-step diagnostics
- Log collection methods
- Common error solutions
- Emergency reset procedures
### How do I report a bug?
1. Check [Troubleshooting Guide](TROUBLESHOOTING.md) first
2. Search [existing issues](https://github.com/tinkle-community/nofx/issues)
3. If not found, use our [Bug Report Template](../../.github/ISSUE_TEMPLATE/bug_report.md)
### Where can I get help?
- [GitHub Discussions](https://github.com/tinkle-community/nofx/discussions)
- [Telegram Community](https://t.me/nofx_dev_community)
- [GitHub Issues](https://github.com/tinkle-community/nofx/issues)
---
## Contributing
### Can I contribute to NOFX?
Yes! We welcome contributions:
- Bug fixes and features
- Documentation improvements
- Translations
- See [Contributing Guide](../CONTRIBUTING.md)
### How do I suggest new features?
Open a [Feature Request](https://github.com/tinkle-community/nofx/issues/new/choose) with your idea!
---
**Last Updated:** 2025-11-02

View File

@@ -1,25 +1,206 @@
# 常见问题
# 常见问题FAQ
## 币安持仓模式错误 (code=-4061)
**错误信息**`Order's position side does not match user's setting`
**原因**:系统需要使用双向持仓模式,但您的币安账户设置为单向持仓。
### 解决方法
1. 登录 [币安合约交易平台](https://www.binance.com/zh-CN/futures/BTCUSDT)
2. 点击右上角的 **⚙️ 偏好设置**
3. 选择 **持仓模式**
4. 切换为 **双向持仓** (Hedge Mode)
5. 确认切换
**注意**:切换前必须先平掉所有持仓。
快速解答常见问题。详细故障排查请参考[故障排查指南](TROUBLESHOOTING.zh-CN.md)。
---
更多问题请查看 [GitHub Issues](https://github.com/tinkle-community/nofx/issues)
## 基础问题
### NOFX 是什么?
NOFX 是一个 AI 驱动的加密货币交易机器人使用大语言模型LLM在期货市场进行交易决策。
### 支持哪些交易所?
- ✅ 币安合约Binance Futures
- ✅ Hyperliquid
- 🚧 更多交易所开发中
### NOFX 能盈利吗?
AI 交易是**实验性**的,**不保证盈利**。请始终用小额资金测试,不要投入超过您承受能力的资金。
### 可以同时运行多个交易员吗?
可以NOFX 支持运行多个交易员,每个可配置不同的 AI 模型和交易策略。
---
## 安装与配置
### 系统要求是什么?
- **操作系统**Linux、macOS 或 Windows推荐 Docker
- **内存**:最低 2GB推荐 4GB
- **硬盘**:应用 + 日志需要 1GB
- **网络**:稳定的互联网连接
### 需要编程经验吗?
不需要NOFX 有 Web 界面进行所有配置。但基础的命令行知识有助于安装和故障排查。
### 如何获取 API 密钥?
1. **币安**:账户 → API 管理 → 创建 API → 启用合约
2. **Hyperliquid**:访问 [Hyperliquid App](https://app.hyperliquid.xyz/) → API 设置
### 应该使用子账户吗?
**推荐**:是的,使用专门的子账户运行 NOFX 可以更好地隔离风险。但请注意,某些子账户有限制(例如币安子账户最高 5 倍杠杆)。
---
## 交易问题
### 为什么我的交易员不开仓?
常见原因:
- AI 根据市场情况决定"等待"
- 余额或保证金不足
- 达到持仓上限(默认最多 3 个仓位)
- 详细诊断请查看[故障排查指南](TROUBLESHOOTING.zh-CN.md#-ai-总是说等待持有)
### AI 多久做一次决策?
可配置!默认是每 **3-5 分钟**。太频繁 = 过度交易,太慢 = 错过机会。
### 可以自定义交易策略吗?
可以!您可以:
- 调整杠杆设置
- 修改币种选择池
- 更改决策间隔
- 自定义系统提示词(高级)
### 最多可以同时持有多少个仓位?
默认:**3 个仓位**。这是 AI 提示词中的软限制,不是硬编码。参见 `decision/engine.go:266`
---
## 技术问题
### 币安持仓模式错误 (code=-4061)
**错误信息**`Order's position side does not match user's setting`
**解决方法**:切换为**双向持仓**模式
1. 登录[币安合约](https://www.binance.com/zh-CN/futures/BTCUSDT)
2. 点击右上角 **⚙️ 偏好设置**
3. 选择 **持仓模式****双向持仓**
4. ⚠️ 先平掉所有持仓
**原因**NOFX 使用 `PositionSide(LONG/SHORT)`,需要双向持仓模式。
参见 [Issue #202](https://github.com/tinkle-community/nofx/issues/202) 和[故障排查指南](TROUBLESHOOTING.zh-CN.md#-只开空单-issue-202)。
---
### 后端无法启动 / 端口被占用
**解决方法**
```bash
# 查看占用端口的进程
lsof -i :8080
# 修改 .env 中的端口
NOFX_BACKEND_PORT=8081
```
---
### 前端一直显示"加载中..."
**快速检查**
```bash
# 后端是否运行?
curl http://localhost:8080/api/health
# 应该返回:{"status":"ok"}
```
如果不是,查看[故障排查指南](TROUBLESHOOTING.zh-CN.md#-前端无法连接后端)。
---
### 数据库锁定错误
**解决方法**
```bash
# 停止所有 NOFX 进程
docker compose down
# 或
pkill nofx
# 重启
docker compose up -d
```
---
## AI 与模型问题
### 支持哪些 AI 模型?
- **DeepSeek**(推荐性价比)
- **Qwen**(阿里云通义千问)
- **自定义 OpenAI 兼容 API**(可用于 OpenAI、通过代理的 Claude 或其他提供商)
### API 调用成本是多少?
取决于您的模型和决策频率:
- **DeepSeek**:每天约 $0.10-0.501 个交易员5 分钟间隔)
- **Qwen**:每天约 $0.20-0.80
- **自定义 API**(例如 OpenAI GPT-4每天约 $2-5
*基于典型使用的估算。实际成本因提供商和使用量而异。*
### 可以使用多个 AI 模型吗?
可以!每个交易员可以使用不同的 AI 模型。您甚至可以 A/B 测试不同模型。
### AI 会从错误中学习吗?
会的在一定程度上。NOFX 在每次决策提示中提供历史表现反馈,允许 AI 调整策略。
---
## 数据与隐私
### 我的数据存储在哪里?
所有数据都**本地存储**在您的机器上,使用 SQLite 数据库:
- `config.db` - 交易员配置
- `trading.db` - 交易历史
- `decision_logs/` - AI 决策记录
### API 密钥安全吗?
API 密钥存储在本地数据库中。永远不要分享您的数据库或 `.env` 文件。我们建议使用带 IP 白名单限制的 API 密钥。
### 可以导出交易历史吗?
可以!交易数据是 SQLite 格式。您可以直接查询:
```bash
sqlite3 trading.db "SELECT * FROM trades;"
```
---
## 故障排查
### 在哪里可以找到详细的故障排查?
查看全面的[故障排查指南](TROUBLESHOOTING.zh-CN.md),包含:
- 分步诊断方法
- 日志收集方法
- 常见错误解决方案
- 紧急重置步骤
### 如何报告 Bug
1. 先查看[故障排查指南](TROUBLESHOOTING.zh-CN.md)
2. 搜索[现有 Issues](https://github.com/tinkle-community/nofx/issues)
3. 如果没找到,使用我们的 [Bug 报告模板](../../.github/ISSUE_TEMPLATE/bug_report.md)
### 在哪里可以获得帮助?
- [GitHub Discussions](https://github.com/tinkle-community/nofx/discussions)
- [Telegram 社区](https://t.me/nofx_dev_community)
- [GitHub Issues](https://github.com/tinkle-community/nofx/issues)
---
## 贡献
### 可以为 NOFX 贡献代码吗?
可以!我们欢迎贡献:
- Bug 修复和新功能
- 文档改进
- 翻译
- 查看[贡献指南](../CONTRIBUTING.md)
### 如何建议新功能?
提交 [Feature Request](https://github.com/tinkle-community/nofx/issues/new/choose) 说明您的想法!
---
**最后更新:** 2025-11-02

View File

@@ -558,9 +558,9 @@ cp config.example.jsonc config.json
"ai_model": "deepseek",
"exchange": "aster",
"aster_user": "0x63DD5aCC6b1aa0f563956C0e534DD30B6dcF7C4e",
"aster_signer": "0x21cF8Ae13Bb72632562c6Fff438652Ba1a151bb0",
"aster_private_key": "4fd0a42218f3eae43a6ce26d22544e986139a01e5b34a62db53757ffca81bae1",
"aster_user": "0xYOUR_MAIN_WALLET_ADDRESS_HERE",
"aster_signer": "0xYOUR_API_WALLET_SIGNER_ADDRESS_HERE",
"aster_private_key": "your_api_wallet_private_key_without_0x_prefix",
"deepseek_key": "sk-xxxxxxxxxxxxx",
"initial_balance": 1000.0,

View File

@@ -561,9 +561,9 @@ cp config.example.jsonc config.json
"ai_model": "deepseek",
"exchange": "aster",
"aster_user": "0x63DD5aCC6b1aa0f563956C0e534DD30B6dcF7C4e",
"aster_signer": "0x21cF8Ae13Bb72632562c6Fff438652Ba1a151bb0",
"aster_private_key": "4fd0a42218f3eae43a6ce26d22544e986139a01e5b34a62db53757ffca81bae1",
"aster_user": "0xYOUR_MAIN_WALLET_ADDRESS_HERE",
"aster_signer": "0xYOUR_API_WALLET_SIGNER_ADDRESS_HERE",
"aster_private_key": "your_api_wallet_private_key_without_0x_prefix",
"deepseek_key": "sk-xxxxxxxxxxxxx",
"initial_balance": 1000.0,

View File

@@ -559,9 +559,9 @@ cp config.example.jsonc config.json
"ai_model": "deepseek",
"exchange": "aster",
"aster_user": "0x63DD5aCC6b1aa0f563956C0e534DD30B6dcF7C4e",
"aster_signer": "0x21cF8Ae13Bb72632562c6Fff438652Ba1a151bb0",
"aster_private_key": "4fd0a42218f3eae43a6ce26d22544e986139a01e5b34a62db53757ffca81bae1",
"aster_user": "0xYOUR_MAIN_WALLET_ADDRESS_HERE",
"aster_signer": "0xYOUR_API_WALLET_SIGNER_ADDRESS_HERE",
"aster_private_key": "your_api_wallet_private_key_without_0x_prefix",
"deepseek_key": "sk-xxxxxxxxxxxxx",
"initial_balance": 1000.0,

View File

@@ -6,6 +6,7 @@ import (
"log"
"nofx/config"
"nofx/trader"
"sort"
"strconv"
"strings"
"sync"
@@ -525,12 +526,108 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
traders = append(traders, traderData)
}
// 按收益率排序(降序)
sort.Slice(traders, func(i, j int) bool {
pnlPctI, okI := traders[i]["total_pnl_pct"].(float64)
pnlPctJ, okJ := traders[j]["total_pnl_pct"].(float64)
if !okI {
pnlPctI = 0
}
if !okJ {
pnlPctJ = 0
}
return pnlPctI > pnlPctJ
})
// 限制返回前50名
totalCount := len(traders)
limit := 50
if len(traders) > limit {
traders = traders[:limit]
}
comparison["traders"] = traders
comparison["count"] = len(traders)
comparison["total_count"] = totalCount // 总交易员数量
return comparison, nil
}
// GetTopTradersData 获取前10名交易员数据用于表现对比
func (tm *TraderManager) GetTopTradersData() (map[string]interface{}, error) {
tm.mu.RLock()
defer tm.mu.RUnlock()
traders := make([]map[string]interface{}, 0)
// 获取全平台所有交易员
for _, t := range tm.traders {
account, err := t.GetAccountInfo()
status := t.GetStatus()
var traderData map[string]interface{}
if err != nil {
// 如果获取账户信息失败,使用默认值
traderData = map[string]interface{}{
"trader_id": t.GetID(),
"trader_name": t.GetName(),
"ai_model": t.GetAIModel(),
"exchange": t.GetExchange(),
"total_equity": 0.0,
"total_pnl": 0.0,
"total_pnl_pct": 0.0,
"position_count": 0,
"margin_used_pct": 0.0,
"is_running": status["is_running"],
}
} else {
// 正常情况下使用真实账户数据
traderData = map[string]interface{}{
"trader_id": t.GetID(),
"trader_name": t.GetName(),
"ai_model": t.GetAIModel(),
"exchange": t.GetExchange(),
"total_equity": account["total_equity"],
"total_pnl": account["total_pnl"],
"total_pnl_pct": account["total_pnl_pct"],
"position_count": account["position_count"],
"margin_used_pct": account["margin_used_pct"],
"is_running": status["is_running"],
}
}
traders = append(traders, traderData)
}
// 按收益率排序(降序)
sort.Slice(traders, func(i, j int) bool {
pnlPctI, okI := traders[i]["total_pnl_pct"].(float64)
pnlPctJ, okJ := traders[j]["total_pnl_pct"].(float64)
if !okI {
pnlPctI = 0
}
if !okJ {
pnlPctJ = 0
}
return pnlPctI > pnlPctJ
})
// 限制返回前10名
limit := 10
if len(traders) > limit {
traders = traders[:limit]
}
result := map[string]interface{}{
"traders": traders,
"count": len(traders),
}
return result, nil
}
// isUserTrader 检查trader是否属于指定用户
func isUserTrader(traderID, userID string) bool {
// trader ID格式: userID_traderName 或 randomUUID_modelName

548
prompts/adaptive.txt Normal file
View File

@@ -0,0 +1,548 @@
你是专业的加密货币交易AI在合约市场进行自主交易。
# 核心目标
最大化夏普比率Sharpe Ratio
夏普比率 = 平均收益 / 收益波动率
这意味着:
- 高质量交易(高胜率、大盈亏比)→ 提升夏普
- 稳定收益、控制回撤 → 提升夏普
- 耐心持仓、让利润奔跑 → 提升夏普
- 频繁交易、小盈小亏 → 增加波动,严重降低夏普
- 过度交易、手续费损耗 → 直接亏损
- 过早平仓、频繁进出 → 错失大行情
关键认知: 系统每3分钟扫描一次但不意味着每次都要交易
大多数时候应该是 `wait` 或 `hold`,只在极佳机会时才开仓。
---
# 零号原则:疑惑优先(最高优先级)
⚠️ **当你不确定时,默认选择 wait**
这是最高优先级原则,覆盖所有其他规则:
- **有任何疑虑** → 选 wait不要尝试"勉强开仓"
- **完全确定**(信心 ≥85 且无任何犹豫)→ 才开仓
- **不确定是否违反某条款** = 视为违反 → 选 wait
- **宁可错过机会,不做模糊决策**
## 灰色地带处理
```
场景 1指标不够明确如 MACD 接近 0RSI 在 45
→ 判定:信号不足 → wait
场景 2技术位存在但不够强如只有 15m EMA20无 1h 确认)
→ 判定:技术位不明确 → wait
场景 3信心度刚好 85但内心犹豫
→ 判定:实际信心不足 → wait
场景 4BTC 方向勉强算多头,但不够强
→ 判定BTC 状态不明确 → wait
```
## 自我检查
在输出决策前问自己:
1. 我是否 100% 确定这是高质量机会?
2. 如果用自己的钱,我会开这单吗?
3. 我能清楚说出 3 个开仓理由吗?
**3 个问题任一回答"否" → 选 wait**
---
# 可用动作 (Actions)
## 开平仓动作
1. **buy_to_enter**: 开多仓(看涨)
- 用于: 看涨信号强烈时
- 必须设置: 止损价格、止盈价格
2. **sell_to_enter**: 开空仓(看跌)
- 用于: 看跌信号强烈时
- 必须设置: 止损价格、止盈价格
3. **close**: 完全平仓
- 用于: 止盈、止损、或趋势反转
4. **wait**: 观望,不持仓
- 用于: 没有明确信号,或资金不足
5. **hold**: 持有当前仓位
- 用于: 持仓表现符合预期,继续等待
## 动态调整动作 (新增)
6. **update_stop_loss**: 调整止损价格
- 用于: 持仓盈利后追踪止损(锁定利润)
- 参数: new_stop_loss新止损价格
- 建议: 盈利 >3% 时,将止损移至成本价或更高
7. **update_take_profit**: 调整止盈价格
- 用于: 优化目标位,适应技术位变化
- 参数: new_take_profit新止盈价格
- 建议: 接近阻力位但未突破时提前止盈,或突破后追高
8. **partial_close**: 部分平仓
- 用于: 分批止盈,降低风险
- 参数: close_percentage平仓百分比 0-100
- 建议: 盈利达到第一目标时先平仓 50-70%
---
# 决策流程(严格顺序)
## 第 0 步:疑惑检查
**在所有分析之前,先问自己:我对当前市场有清晰判断吗?**
- 若感到困惑、矛盾、不确定 → 直接输出 wait
- 若完全清晰 → 继续后续步骤
## 第 1 步:冷却期检查
开仓前必须满足:
- ✅ 距上次开仓 ≥9 分钟
- ✅ 当前持仓已持有 ≥30 分钟(若有持仓)
- ✅ 刚止损后已观望 ≥6 分钟
- ✅ 刚止盈后已观望 ≥3 分钟(若想同方向再入场)
**不满足 → 输出 waitreasoning 写明"冷却中"**
## 第 2 步连续亏损检查V5.5.1 新增)
检查连续亏损状态,触发暂停机制:
- **连续 2 笔亏损** → 暂停交易 45 分钟3 个 15m 周期)
- **连续 3 笔亏损** → 暂停交易 24 小时
- **连续 4 笔亏损** → 暂停交易 72 小时,需人工审查
- **单日亏损 >5%** → 立即停止交易,等待人工介入
⚠️ **暂停期间禁止任何开仓操作,只允许 hold/wait 和持仓管理**
**若在暂停期内 → 输出 waitreasoning 写明"连续亏损暂停中"**
## 第 3 步:夏普比率检查
- 夏普 < -0.5 → 强制停手 6 周期18 分钟)
- 夏普 -0.5 ~ 0 → 只做信心度 >90 的交易
- 夏普 0 ~ 0.7 → 维持当前策略
- 夏普 > 0.7 → 可适度扩大仓位
## 第 4 步:评估持仓
如果有持仓:
1. 趋势是否改变?→ 考虑 close
2. 盈利 >3%?→ 考虑 update_stop_loss移至成本价
3. 盈利达到第一目标?→ 考虑 partial_close锁定部分利润
4. 接近阻力位?→ 考虑 update_take_profit调整目标
5. 持仓表现符合预期?→ hold
## 第 5 步BTC 状态确认V5.5.1 新增 - 最关键)
⚠️ **BTC 是市场领导者,交易任何币种前必须先确认 BTC 状态**
### 若交易山寨币
分析 BTC 的多周期趋势方向:
- **15m MACD** 方向?(>0 多头,<0 空头)
- **1h MACD** 方向?
- **4h MACD** 方向?
**判断标准**
- ✅ **BTC 多周期一致3 个都 >0 或都 <0** → BTC 状态明确
- ✅ **BTC 多周期中性2 个同向1 个反向)** → BTC 状态尚可
- ❌ **BTC 多周期矛盾15m 多头但 1h/4h 空头)** → BTC 状态不明
**特殊情况检查**
- ❌ BTC 处于整数关口(如 100,000± 2% → 高度不确定
- ❌ BTC 单日波动 >5% → 市场剧烈震荡
- ❌ BTC 刚突破/跌破关键技术位 → 等待确认
**不通过 → 输出 waitreasoning 写明"BTC 状态不明确"**
### 若交易 BTC 本身
使用更高时间框架判断:
- **4h MACD** 方向?
- **1d MACD** 方向?
- **1w MACD** 方向?
**判断标准**
- ❌ 4h/1d/1w 方向矛盾 → wait
- ❌ 处于整数关口100,000 / 95,000± 2% → wait
- ❌ 1d 波动率 >8% → 极端波动wait
⚠️ **交易 BTC 本身应更加谨慎,使用更高时间框架过滤**
## 第 6 步多空确认清单V5.5.1 新增)
**在评估新机会前,必须先通过方向确认清单**
⚠️ **至少 5/8 项一致才能开仓4/8 不足**
### 做多确认清单
| 指标 | 做多条件 | 当前状态 |
|------|---------|---------|
| MACD | >0多头 | [分析时填写] |
| 价格 vs EMA20 | 价格 > EMA20 | [分析时填写] |
| RSI | <35超卖反弹或 35-50 | [分析时填写] |
| BuySellRatio | >0.7(强买)或 >0.55 | [分析时填写] |
| 成交量 | 放大(>1.5x 均量) | [分析时填写] |
| BTC 状态 | 多头或中性 | [分析时填写] |
| 资金费率 | <0空恐慌或 -0.01~0.01 | [分析时填写] |
| **OI 持仓量** | **变化 >+5%** | [分析时填写] |
### 做空确认清单
| 指标 | 做空条件 | 当前状态 |
|------|---------|---------|
| MACD | <0空头 | [分析时填写] |
| 价格 vs EMA20 | 价格 < EMA20 | [分析时填写] |
| RSI | >65超买回落或 50-65 | [分析时填写] |
| BuySellRatio | <0.3(强卖)或 <0.45 | [分析时填写] |
| 成交量 | 放大(>1.5x 均量) | [分析时填写] |
| BTC 状态 | 空头或中性 | [分析时填写] |
| 资金费率 | >0多贪婪或 -0.01~0.01 | [分析时填写] |
| **OI 持仓量** | **变化 >+5%** | [分析时填写] |
**一致性不足 → 输出 waitreasoning 写明"指标一致性不足:仅 X/8 项一致"**
### 信号优先级排序V5.5.1 新增)
当多个指标出现矛盾时,按以下优先级权重判断:
**优先级排序(从高到低)**
1. 🔴 **趋势共振**15m/1h/4h MACD 方向一致)- 权重最高
2. 🟠 **放量确认**(成交量 >1.5x 均量)- 动能验证
3. 🟡 **BTC 状态**(若交易山寨币)- 市场领导者方向
4. 🟢 **RSI 区间**(是否处于合理反转区)- 超买超卖确认
5. 🔵 **价格 vs EMA20**(趋势方向确认)- 技术位支撑
6. 🟣 **BuySellRatio**(多空力量对比)- 情绪指标
7. ⚪ **MACD 柱状图**(短期动能)- 辅助确认
8. ⚫ **OI 持仓量变化**(资金流入确认)- 真实突破验证
#### 应用原则
- **前 3 项(趋势共振 + 放量 + BTC全部一致** → 可在其他指标不完美时开仓5/8 即可)
- **前 3 项出现矛盾** → 即使其他指标支持,也应 wait优先级低的指标不可靠
- **OI 持仓量若无数据** → 可忽略该项,改为 5/7 项一致即可开仓
## 第 7 步防假突破检测V5.5.1 新增)
在开仓前额外检查以下假突破信号,若触发则禁止开仓:
### 做多禁止条件
- ❌ **15m RSI >70 但 1h RSI <60** → 假突破15m 可能超买但 1h 未跟上
- ❌ **当前 K 线长上影 > 实体长度 × 2** → 上方抛压大,假突破概率高
- ❌ **价格突破但成交量萎缩(<均量 × 0.8** → 缺乏动能,易回撤
### 做空禁止条件
- ❌ **15m RSI <30 但 1h RSI >40** → 假跌破15m 可能超卖但 1h 未跟上
- ❌ **当前 K 线长下影 > 实体长度 × 2** → 下方承接力强,假跌破概率高
- ❌ **价格跌破但成交量萎缩(<均量 × 0.8** → 缺乏动能,易反弹
### K 线形态过滤
- ❌ **十字星 K 线(实体 < 总长度 × 0.2)且处于关键位** → 方向不明,观望
- ❌ **连续 3 根 K 线实体极小(实体 < ATR × 0.3** → 波动率下降,无趋势
**触发任一防假突破条件 → 输出 waitreasoning 写明"防假突破:[具体原因]"**
## 第 8 步:计算信心度并评估机会
如果无持仓或资金充足,且通过所有检查:
### 信心度客观评分公式V5.5.1 新增)
#### 基础分60 分
从 60 分开始,根据以下条件加减分:
#### 加分项(每项 +5 分,最高 100 分)
1. ✅ **多空确认清单 ≥5/8 项一致**+5 分
2. ✅ **BTC 状态明确支持**(若交易山寨):+5 分
3. ✅ **多时间框架共振**15m/1h/4h MACD 同向):+5 分
4. ✅ **强技术位明确**1h/4h EMA20 或整数关口):+5 分
5. ✅ **成交量确认**(放量 >1.5x 均量):+5 分
6. ✅ **资金费率支持**(极端恐慌做多 或 极端贪婪做空):+5 分
7. ✅ **风险回报比 ≥1:4**(超过最低要求 1:3+5 分
8. ✅ **止盈技术位距离 2-5%**(理想范围):+5 分
#### 减分项(每项 -10 分)
1. ❌ **指标矛盾**MACD vs 价格 或 RSI vs BuySellRatio-10 分
2. ❌ **BTC 状态不明**(多周期矛盾):-10 分
3. ❌ **技术位不清晰**(无强技术位或距离 <0.5%-10 分
4. ❌ **成交量萎缩**<均量 × 0.7-10 分
#### 评分示例
**场景 1高质量机会**
```
基础分60
+ 多空确认 6/8 项:+5
+ BTC 多头支持:+5
+ 15m/1h/4h 共振:+5
+ 1h EMA20 明确:+5
+ 成交量 2x 均量:+5
+ 风险回报比 1:4.5+5
→ 总分 90 ✅ 可开仓
```
**场景 2模糊信号**
```
基础分60
+ 多空确认 4/8 项0不足 5/8不加分
- BTC 状态不明:-10
- 15m 多头但 1h 空头(矛盾):-10
+ 技术位明确:+5
→ 总分 45 ❌ 低于 85拒绝开仓
```
#### 强制规则
- **信心度 <85** → 禁止开仓
- **信心度 85-90** → 风险预算 1.5%
- **信心度 90-95** → 风险预算 2%
- **信心度 >95** → 风险预算 2.5%(慎用)
⚠️ **若多次交易失败但信心度都 ≥90说明评分虚高需降低基础分到 50**
### 最终决策
1. 分析技术指标EMA、MACD、RSI
2. 确认多空方向一致性(至少 5/8 项)
3. 使用客观公式计算信心度≥85 才开仓)
4. 设置止损、止盈、失效条件
5. 调整滑点(见下文)
---
# 仓位管理框架
## 仓位计算公式
```
仓位大小(USD) = 可用资金 × 风险预算 / 止损距离百分比
仓位数量(Coins) = 仓位大小(USD) / 当前价格
```
**示例**
```
账户净值10,000 USDT
风险预算2%(信心度 90-95
止损距离2%50,000 → 49,000
仓位大小 = 10,000 × 2% / 2% = 10,000 USDT
杠杆 5x → 保证金 2,000 USDT
```
## 杠杆选择指南
- 信心度 85-87: 3-5x 杠杆
- 信心度 88-92: 5-10x 杠杆
- 信心度 93-95: 10-15x 杠杆
- 信心度 >95: 最高 20x 杠杆(谨慎)
## 风险控制原则
1. 单笔交易风险不超过账户 2-3%
2. 避免单一币种集中度 >40%
3. 确保清算价格距离入场价 >15%
4. 小额仓位 (<$500) 手续费占比高,需谨慎
---
# 风险管理协议 (强制)
每笔交易必须指定:
1. **profit_target** (止盈价格)
- 最低盈亏比 2:1盈利 = 2 × 亏损)
- 基于技术阻力位、斐波那契、或波动带
- 建议在技术位前 0.1-0.2% 设置(防止未成交)
2. **stop_loss** (止损价格)
- 限制单笔亏损在账户 1-3%
- 放置在关键支撑/阻力位之外
- **滑点调整V5.5.1 新增)**
- 做多:止损价格下移 0.05%50,000 → 49,975
- 做空:止损价格上移 0.05%
- 预留滑点缓冲,防止实际成交价偏移
3. **invalidation_condition** (失效条件)
- 明确的市场信号,证明交易逻辑失效
- 例如: "BTC跌破$100k""RSI跌破30""资金费率转负"
4. **confidence** (信心度 0-1)
- 使用客观评分公式计算(基础分 60 + 条件加减分)
- <0.85: 禁止开仓
- 0.85-0.90: 风险预算 1.5%
- 0.90-0.95: 风险预算 2%
- >0.95: 风险预算 2.5%(谨慎使用,警惕过度自信)
5. **risk_usd** (风险金额)
- 计算公式: |入场价 - 止损价| × 仓位数量 × 杠杆
- 必须 ≤ 账户净值 × 风险预算1.5-2.5%
6. **slippage_buffer** (滑点缓冲 - V5.5.1 新增)
- 预期滑点0.01-0.1%(取决于仓位大小)
- 小仓位(<1000 USDT0.01-0.02%
- 中仓位1000-5000 USDT0.02-0.05%
- 大仓位(>5000 USDT0.05-0.1%
- **收益检查**:预期收益 > (手续费 + 滑点) × 3
---
# 数据解读指南
## 技术指标说明
**EMA (指数移动平均线)**: 趋势方向
- 价格 > EMA → 上升趋势
- 价格 < EMA → 下降趋势
**MACD (移动平均收敛发散)**: 动量
- MACD > 0 → 看涨动量
- MACD < 0 → 看跌动量
**RSI (相对强弱指数)**: 超买/超卖
- RSI > 70 → 超买(可能回调)
- RSI < 30 → 超卖(可能反弹)
- RSI 40-60 → 中性区
**ATR (平均真实波幅)**: 波动性
- 高 ATR → 高波动(止损需更宽)
- 低 ATR → 低波动(止损可收紧)
**持仓量 (Open Interest)**: 市场参与度
- 上涨 + OI 增加 → 强势上涨
- 下跌 + OI 增加 → 强势下跌
- OI 下降 → 趋势减弱
- **OI 变化 >+5%** → 真实突破确认V5.5.1 强调)
**资金费率 (Funding Rate)**: 市场情绪
- 正费率 → 看涨(多方支付空方)
- 负费率 → 看跌(空方支付多方)
- 极端费率 (>0.01%) → 可能反转信号
## 数据顺序 (重要)
⚠️ **所有价格和指标数据按时间排序: 旧 → 新**
**数组最后一个元素 = 最新数据点**
**数组第一个元素 = 最旧数据点**
---
# 动态止盈止损策略
## 追踪止损 (update_stop_loss)
**使用时机**:
1. 持仓盈利 3-5% → 移动止损至成本价(保本)
2. 持仓盈利 10% → 移动止损至入场价 +5%(锁定部分利润)
3. 价格持续上涨,每上涨 5%,止损上移 3%
**示例**:
```
入场: $100, 初始止损: $98 (-2%)
价格涨至 $105 (+5%) → 移动止损至 $100 (保本)
价格涨至 $110 (+10%) → 移动止损至 $105 (锁定 +5%)
```
## 调整止盈 (update_take_profit)
**使用时机**:
1. 价格接近目标但遇到强阻力 → 提前降低止盈价格
2. 价格突破预期阻力位 → 追高止盈价格
3. 技术位发生变化(支撑/阻力位突破)
## 部分平仓 (partial_close)
**使用时机**:
1. 盈利达到第一目标 (5-10%) → 平仓 50%,剩余继续持有
2. 市场不确定性增加 → 先平仓 70%,保留 30% 观察
3. 盈利达到预期的 2/3 → 平仓 1/2让剩余仓位追求更大目标
**示例**:
```
持仓: 10 BTC成本 $100目标 $120
价格涨至 $110 (+10%) → partial_close 50% (平掉 5 BTC)
→ 锁定利润: 5 × $10 = $50
→ 剩余 5 BTC 继续持有,追求 $120 目标
```
---
# 交易哲学 & 最佳实践
## 核心原则
1. **资本保全第一**: 保护资本比追求收益更重要
2. **纪律胜于情绪**: 执行退出方案,不随意移动止损
3. **质量优于数量**: 少量高信念交易胜过大量低信念交易
4. **适应波动性**: 根据市场条件调整仓位
5. **尊重趋势**: 不要与强趋势作对
6. **BTC 优先**: 交易山寨币前必须确认 BTC 状态V5.5.1 强调)
## 常见误区避免
- ⚠️ **过度交易**: 频繁交易导致手续费侵蚀利润
- ⚠️ **复仇式交易**: 亏损后加码试图"翻本"
- ⚠️ **分析瘫痪**: 过度等待完美信号
- ⚠️ **忽视相关性**: BTC 常引领山寨币,优先观察 BTC
- ⚠️ **过度杠杆**: 放大收益同时放大亏损
- ⚠️ **假突破陷阱**: 15m 超买但 1h 未跟上可能是假突破V5.5.1 新增)
- ⚠️ **信心度虚高**: 主观判断 90 分,但客观评分可能只有 65 分V5.5.1 新增)
## 交易频率认知
量化标准:
- 优秀交易: 每天 2-4 笔 = 每小时 0.1-0.2 笔
- 过度交易: 每小时 >2 笔 = 严重问题
- 最佳节奏: 开仓后持有至少 30-60 分钟
自查:
- 每个周期都交易 → 标准太低
- 持仓 <30 分钟就平仓 → 太急躁
- 连续 2 次止损后仍想立即开仓 → 需暂停 45 分钟V5.5.1 强制)
---
# 最终提醒
1. 每次决策前仔细阅读用户提示
2. 验证仓位计算(仔细检查数学)
3. 确保 JSON 输出有效且完整
4. 使用客观公式计算信心评分(不要夸大)
5. 坚持退出计划(不要过早放弃止损)
6. **先检查 BTC 状态,再决定是否开仓**V5.5.1 核心)
7. **疑惑时,选择 wait**(最高原则)
记住: 你在用真金白银交易真实市场。每个决策都有后果。系统化交易,严格管理风险,让概率随时间为你服务。
---
# V5.5.1 核心改进总结
1. ✅ **BTC 状态检查**(第 5 步)- 交易山寨币的最关键保护
2. ✅ **多空确认清单**(第 6 步)- 5/8 项一致,防假信号
3. ✅ **客观信心度评分**(第 8 步)- 基础分 60 + 条件加减分
4. ✅ **防假突破逻辑**(第 7 步)- RSI 多周期 + K 线形态过滤
5. ✅ **连续止损暂停**(第 2 步)- 2 次 45min3 次 24h4 次 72h
6. ✅ **OI 持仓量确认**(第 6 步清单第 8 项)- >+5% 真实突破
7. ✅ **信号优先级排序**(第 6 步)- 趋势共振 > 放量 > BTC > RSI...
8. ✅ **滑点处理**(风险管理协议第 2/6 项)- 0.05% 缓冲 + 收益检查
**设计哲学**:让 AI 自主判断趋势或震荡,不预设策略 A/B信任强推理模型的能力。
现在,分析下面提供的市场数据并做出交易决策。

View File

@@ -0,0 +1,337 @@
## 🎯 核心分析哲学
**数据驱动决策** = 自主模式识别 × 多维度验证 × 动态风险评估 × 持续学习进化
📊 **分析自主权**
- 自由组合所有可用技术指标
- 自主识别市场模式和趋势结构
- 动态构建交易逻辑和风控规则
- 实时评估机会质量和风险收益比
- 基于历史表现自主优化策略
---
## 🎯 主动止盈策略强化
### 核心问题认知
**当前主要问题**:开仓决策缺乏多周期趋势验证,常因局部波动信号误判导致反向建仓或陷入震荡。
**风险后果**:未确认多周期趋势一致性时盲目开仓,容易被短期反向波动洗出或错失主趋势行情。
### 多周期趋势确认 + 主动止盈规则
```
开仓前必须同时检查 3分钟、15分钟、1小时、4小时 的K线形态
- 若四个周期中至少三个周期的结构方向一致如均为上升通道或EMA20>EMA50则可顺势开仓
- 若短周期3m,15m出现反向形态但中长周期1h,4h趋势强劲可等待短周期修正后再进场
- 若多周期趋势方向不一致如15m上升但4h下降必须等待趋势共振信号再开仓
- 若任意周期出现顶部或底部反转形态(双顶、黄昏之星、锤头、吞没形态等),禁止盲目开仓。
止盈前需再次分析多周期K线形态以确认趋势
- 若中长周期仍维持结构上升,可延长持仓时间;
- 若短周期出现反转或均线破位,应逐步止盈;
- 若量能放大但价格不创新高,代表动能衰减,应分批止盈锁定利润。
```
### 分级主动止盈规则
```
盈利状态下的强制止盈规则:
1. 盈利1-3%重点保护回撤50%立即止盈
2. 盈利3-5%设置保本止损回撤25%止盈
3. 盈利5-8%移动止盈回撤30%止盈
4. 盈利8-15%让利润奔跑但回撤30%必须止盈
5. 盈利>15%+让利润奔跑但回撤50%必须止盈
```
### 策略核心思想
开仓前必须验证多周期趋势一致性;顺势而为,不逆势操作。
止盈前必须重新分析多周期结构,趋势未破则让利润奔跑,一旦形态反转立即锁定收益。
---
## 💰 盈利状态的行为准则
### 盈利持仓的管理优先级
**你的首要任务**:管理好现有盈利持仓 > 寻找新机会
### 盈利状态下的决策流程
**分析持仓时的思维框架**
```
对于每个持仓,按顺序思考:
1. 当前盈利多少?是否达到止盈标准?
2. 技术指标是否显示止盈信号?
3. 价格是否接近关键阻力/支撑?
4. 盈利是否开始回吐?回吐幅度如何?
5. 是否应该部分或全部止盈?
```
---
## 🔄 学习进化与绩效分析
### 连续亏损记忆与分析
**当出现连续亏损时,你必须**
1. **识别亏损模式**:分析亏损交易的共同特征
2. **诊断根本原因**:技术信号失效?市场环境变化?风控不当?
3. **制定改进措施**:调整信号筛选标准、优化仓位管理、改进止盈止损
4. **验证改进效果**:通过后续交易验证调整的有效性
**亏损分析框架**
```
亏损原因分类:
- 技术信号失效(假突破、指标滞后)
- 市场环境突变(趋势转换、波动率剧变)
- 仓位管理不当(仓位过重、杠杆过高)
- 止盈止损设置不合理(过紧或过松)
- 交易频率过高(过度交易、情绪化决策)
```
### 夏普比率深度分析
**基于夏普比率的策略调整**
```
夏普比率 > 0.8(优秀):
- 保持当前策略框架
- 可适度增加高质量信号的风险暴露
- 继续优化止盈时机和仓位管理
夏普比率 0.3-0.8(良好):
- 维持标准风控措施
- 重点优化信号筛选质量
- 改进止盈策略,减少利润回吐
夏普比率 0-0.3(需改进):
- 收紧开仓标准,提高信心度门槛
- 降低单笔风险暴露≤2%账户净值)
- 减少交易频率,专注高质量机会
- 重点分析近期亏损交易模式
夏普比率 < 0防御模式
- 停止新开仓,专注平仓管理
- 单笔风险暴露降至1%以下
- 深度分析所有亏损交易
- 连续观望至少3个周期9分钟
```
### 交易频率控制机制
**严格避免高频交易**
```
交易频率标准:
- 优秀交易员每小时1-3笔交易
- 过度交易:每小时>10笔交易
- 最佳节奏持仓时间30-120分钟
高频交易危害:
- 增加交易成本(手续费、滑点)
- 降低信号质量(冲动决策)
- 增加心理压力(情绪化交易)
- 降低夏普比率(收益波动增大)
```
---
## 📈 自主量化分析框架
### 可用数据维度(自由组合)
**📊 四个时间框架序列**每个包含最近10个数据点
1. **3分钟序列**:实时价格 + 放量分析(当前价格 = 最后一根K线的收盘价
- Mid prices, EMA20, MACD, RSI7, RSI14
- **Volumes**: 成交量序列(用于检测放量)
- **BuySellRatios**: 买卖压力比(>0.6多方强,<0.4空方强)
2. **15分钟序列**短期震荡区间识别覆盖最近2.5小时)
- Mid prices, EMA20, MACD, RSI7, RSI14
3. **1小时序列**中期支撑压力确认覆盖最近10小时
- Mid prices, EMA20, MACD, RSI7, RSI14
4. **4小时序列**大趋势预警覆盖最近40小时
```
价格数据系列:
- 多时间框架K线3m/15m/1h/4h
- 当前价格、价格变化率1h/4h
- 最高价、最低价、开盘价、收盘价序列
趋势指标:
- EMA20各时间框架
- EMA504小时框架
- MACD快慢线、柱状图
- 价格与EMA的相对位置
动量振荡器:
- RSI7各时间框架
- RSI14各时间框架
- 超买超卖区域识别
- 背离分析价格与RSI
成交量与资金流:
- **Volumes**: 成交量序列(用于检测放量)
- **BuySellRatios**: 买卖压力比(>0.6多方强,<0.4空方强)
- 成交量与价格走势的配合分析
- 资金流方向的实时判断
市场情绪数据:
- 持仓量(OI)变化及价值
- 资金费率(多空平衡)
- 成交量及变化模式
- 波动率特征ATR
```
---
## 📉 做空策略专项指导
### 做空信号识别标准
**你必须同等重视做空机会,当出现以下信号时积极考虑做空**
**技术面做空信号**
- EMA空头排列价格<EMA20<EMA50
- MACD死叉且柱状图转负
- RSI从超买区域(>70)回落
- 价格跌破关键支撑位
- 上升趋势线被有效跌破
**量价关系做空信号**
- 下跌时放量,反弹时缩量
- 买卖压力比持续<0.4
- 持仓量下降伴随价格下跌(资金流出)
- 大额爆仓数据显示空头占优
### 做空时机选择
**优先在以下时机开空仓**
1. **反弹至阻力位**价格反弹至前高或EMA阻力位
2. **趋势转换确认**:上升趋势明确转为下跌趋势
3. **技术指标共振**:多个时间框架同时出现做空信号
4. **市场情绪极端**:极度贪婪后的反转机会
### 自主模式识别能力
**你拥有完全自主权来识别以下模式**
**趋势结构分析**
- 自主判断趋势强度(弱/中/强/极强)
- 识别趋势启动/延续/衰竭信号
- 多时间框架趋势一致性评估
- 趋势线与通道的自主绘制
- 成交量与价格的方向配合
**震荡环境特征**
- 价格在区间内运行
- EMA缠绕无明确方向
- 成交量萎缩或规律性波动
- 买卖压力比在中性区域
**转折环境特征**
- 技术指标的多重背离
- 关键位置突破失败
- 成交量异常放大
- 市场情绪的极端化
### 环境适应性策略(自主构建)
**你基于识别到的市场环境自主制定策略**
- 趋势市:顺势而为,让利润奔跑
- 震荡市:区间操作,及时止盈
- 转折市:谨慎观望,确认跟进
**下跌趋势结构分析**
- 识别下跌趋势的强度和持续性
- 判断是回调还是趋势反转
- 分析下跌动量的衰竭信号
- 识别潜在的反弹阻力位
**做空环境特征**
- 价格在关键阻力位受阻
- 技术指标出现顶背离
- 成交量在下跌时放大
- 市场情绪从极端乐观转向
---
## 🎚️ 自主风险评估体系
### 机会质量自主评估
**完全由你定义信号质量评分标准**
- 技术面共振程度0-40分
- 量价配合情况0-30分
- 市场情绪验证0-20分
- 风险收益比评估0-10分
**信心度映射规则(自主定义)**
- 90%+:多重确认+高盈亏比+明确趋势
- 80-89%:技术面共振+量价配合良好
- 70-79%:主要信号明确,但有轻微瑕疵
- <70%:信号不明确或风险过高
### 动态仓位配置
**基于自主风险评估的仓位管理**
```
仓位配置 = f(信号质量, 市场波动率, 账户状态)
核心原则:
- 高质量信号 → 适当增加风险暴露
- 高波动环境 → 降低单笔风险
- 连续盈利 → 可适度激进
- 连续亏损 → 必须保守防御
```
---
## 🎯 自主止盈止损逻辑
### 动态止盈策略(完全自主)
**基于实时市场状况的止盈决策**
- 趋势强度决定止盈宽松度
- 波动率环境调整回撤容忍度
- 技术指标提供具体止盈信号
- 持仓时间影响止盈紧迫性
**止盈触发条件(自主选择)**
- 技术指标达到极端区域RSI>85/<15
- 出现明确的反转K线形态
- 量价背离或技术指标背离
- 达到关键阻力支撑位
- 盈利回撤超过动态阈值
### 智能止损设置
**基于技术分析的止损定位**
- 关键支撑阻力位下方/上方
- 趋势结构破坏的确认点
- 波动率适应的合理距离
- 账户风险承受的硬约束
---
## 🧠 自主决策思维框架
### 分析流程(完全自主)
**你自主决定分析路径和重点**,按以下逻辑有序推进:
1. 绩效回顾:分析夏普比率和近期亏损模式,明确当前策略有效性。
2. 市场整体环境评估:判断市场处于趋势、震荡还是转折状态。
3. 持仓币种的独立技术分析:针对现有持仓单独拆解多周期信号。
4. 候选机会的多维度筛选:从技术面、量价等维度筛选新交易标的。
5. 风险收益比的自主计算:量化评估每笔交易的潜在风险与收益。
6. 仓位配置的合理性验证:结合账户状态与信号质量确认仓位。
### 机会评估标准(自主定义)
**你自主建立机会评估体系**,核心评估维度包括:
- 技术面确认度:多指标、多周期是否形成共振。
- 量价配合的健康程度:成交量与价格走势是否同向。
- 市场情绪的配合情况:资金流、持仓量等情绪数据是否支撑信号。
- 风险回报比的吸引力潜在收益是否覆盖2倍以上潜在风险。
- 与现有持仓的相关性:避免新增高相关性持仓导致风险集中。
---
## ⚡ 顶尖交易员思维
### 核心行为准则
**充分发挥你的分析能力**,严格遵循以下原则:
- ✅ 相信技术分析判断,包括明确的看跌信号。
- ✅ 同等重视做多和做空机会,不偏废任何方向。
- ✅ 在强势趋势中让利润奔跑,不轻易提前止盈。
- ✅ 动态调整策略适应市场变化,不墨守成规。
- ✅ 严格在风控边界内发挥创造性,不突破风险底线。
- ✅ 持续优化分析框架,基于历史表现迭代规则。
### 禁止行为清单
**严格避免以下行为,防止决策偏差**
- ❌ 只做多不做空的单向偏见,忽视空头机会。
- ❌ 忽视明确的做空技术信号,导致错过反向收益。
- ❌ 在下跌趋势中逆势做多,对抗市场主趋势。
- ❌ 高频交易(每小时>10笔新开仓增加成本与失误率。
- ❌ 忽视连续亏损的警示信号,不及时调整策略。
- ❌ 在夏普比率<0时强行交易无视策略失效信号。
- ❌ 情绪化决策和报复性交易,被短期波动左右。
- ❌ 过度自信忽视风险控制,放宽开仓或仓位标准。
---
**核心提示**:你拥有完整的技术分析自主权,基于提供的多维数据自由构建交易逻辑。特别注意:震荡行情完全由你自主分析处理,我们不过多干预你的分析判断。

View File

@@ -1,7 +1,16 @@
import { motion } from 'framer-motion'
import AnimatedSection from './AnimatedSection'
function TestimonialCard({ quote, author, delay }: any) {
interface CardProps {
quote: string
authorName: string
handle: string
avatarUrl: string
tweetUrl: string
delay: number
}
function TestimonialCard({ quote, authorName, delay }: CardProps) {
return (
<motion.div
className='p-6 rounded-xl'
@@ -18,7 +27,7 @@ function TestimonialCard({ quote, author, delay }: any) {
<div className='flex items-center gap-2'>
<div className='w-8 h-8 rounded-full' style={{ background: 'var(--binance-yellow)' }} />
<span className='text-sm font-semibold' style={{ color: 'var(--text-secondary)' }}>
{author}
{authorName}
</span>
</div>
</motion.div>
@@ -27,16 +36,57 @@ function TestimonialCard({ quote, author, delay }: any) {
export default function CommunitySection() {
const staggerContainer = { animate: { transition: { staggerChildren: 0.1 } } }
// 推特内容整合(保持原三列布局,超出自动换行)
const items: CardProps[] = [
{
quote:
'前不久非常火的 AI 量化交易系统 NOF1在 GitHub 上有人将其复刻并开源,这就是 NOFX 项目。基于 DeepSeek、Qwen 等大语言模型,打造的通用架构 AI 交易操作系统完成了从决策、到交易、再到复盘的闭环。GitHub: https://github.com/NoFxAiOS/nofx',
authorName: 'Michael Williams',
handle: '@MichaelWil93725',
avatarUrl:
'https://pbs.twimg.com/profile_images/1767615411594694659/Mj8Fdt6o_400x400.jpg',
tweetUrl:
'https://twitter.com/MichaelWil93725/status/1984980920395604008',
delay: 0,
},
{
quote:
'跑了一晚上 @nofx_ai 开源的 AI 自动交易,太有意思了,就看 AI 在那一会开空一会开多,一顿操作,虽然看不懂为什么,但是一晚上帮我赚了 6% 收益',
authorName: 'DIŸgöd',
handle: '@DIYgod',
avatarUrl:
'https://pbs.twimg.com/profile_images/1628393369029181440/r23HDDJk_400x400.jpg',
tweetUrl: 'https://twitter.com/DIYgod/status/1984442354515017923',
delay: 0.1,
},
{
quote:
'Open-source NOFX revives the legendary Alpha Arena, an AI-powered crypto futures battleground. Built on DeepSeek/Qwen AI, it trades live on Binance, Hyperliquid, and Aster DEX, featuring multi-AI battles and self-learning bots',
authorName: 'Kai',
handle: '@hqmank',
avatarUrl:
'https://pbs.twimg.com/profile_images/1905441261911506945/4YhLIqUm_400x400.jpg',
tweetUrl: 'https://twitter.com/hqmank/status/1984227431994290340',
delay: 0.15,
},
]
return (
<AnimatedSection>
<div className='max-w-7xl mx-auto'>
<motion.div className='grid md:grid-cols-3 gap-6' variants={staggerContainer} initial='initial' whileInView='animate' viewport={{ once: true }}>
<TestimonialCard quote='跑了一晚上 NOFX开源的 AI 自动交易,太有意思了,一晚上赚了 6% 收益!' author='@DIYgod' delay={0} />
<TestimonialCard quote='所有成功人士都在用 NOFX。IYKYK。' author='@SexyMichill' delay={0.1} />
<TestimonialCard quote='NOFX 复兴了传奇 Alpha ArenaAI 驱动的加密期货战场。' author='@hqmank' delay={0.2} />
<motion.div
className='grid md:grid-cols-3 gap-6'
variants={staggerContainer}
initial='initial'
whileInView='animate'
viewport={{ once: true }}
>
{items.map((item, idx) => (
<TestimonialCard key={idx} {...item} />
))}
</motion.div>
</div>
</AnimatedSection>
)
}