Full Code of NoFxAiOS/nofx for AI

dev 79a513470b49 cached
497 files
4.2 MB
1.1M tokens
2816 symbols
1 requests
Download .txt
Showing preview only (4,460K chars total). Download the full file or copy to clipboard to get everything.
Repository: NoFxAiOS/nofx
Branch: dev
Commit: 79a513470b49
Files: 497
Total size: 4.2 MB

Directory structure:
gitextract_lfraba5v/

├── .dockerignore
├── .github/
│   ├── CLA.md
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bounty_claim.md
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PR_TITLE_GUIDE.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   ├── README.md
│   │   ├── backend.md
│   │   ├── docs.md
│   │   ├── frontend.md
│   │   └── general.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── SECURITY.md
│   ├── labeler.yml
│   ├── labels.yml
│   └── workflows/
│       ├── README.md
│       ├── docker-build.yml
│       ├── pr-checks-comment.yml
│       ├── pr-checks-run.yml
│       ├── pr-checks.yml
│       ├── pr-docker-check.yml
│       ├── pr-docker-compose-healthcheck.yml
│       ├── pr-go-test-coverage.yml
│       ├── pr-template-suggester.yml
│       ├── scripts/
│       │   ├── calculate_coverage.py
│       │   ├── comment_pr.py
│       │   └── requirements.txt
│       └── test.yml
├── .gitignore
├── .husky/
│   ├── _/
│   │   └── husky.sh
│   └── pre-commit
├── CHANGELOG.md
├── CHANGELOG.zh-CN.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DISCLAIMER.md
├── Dockerfile.railway
├── ENCRYPTION_README.md
├── LICENSE
├── Makefile
├── README.ja.md
├── README.md
├── SECURITY.md
├── api/
│   ├── crypto_handler.go
│   ├── errors.go
│   ├── handler_ai_model.go
│   ├── handler_competition.go
│   ├── handler_exchange.go
│   ├── handler_klines.go
│   ├── handler_order.go
│   ├── handler_telegram.go
│   ├── handler_trader.go
│   ├── handler_trader_config.go
│   ├── handler_trader_status.go
│   ├── handler_user.go
│   ├── handler_wallet.go
│   ├── route_registry.go
│   ├── server.go
│   ├── server_test.go
│   ├── strategy.go
│   ├── traderid_test.go
│   ├── utils.go
│   └── utils_test.go
├── auth/
│   └── auth.go
├── config/
│   └── config.go
├── crypto/
│   └── crypto.go
├── docker/
│   ├── Dockerfile.backend
│   └── Dockerfile.frontend
├── docker-compose.prod.yml
├── docker-compose.stable.yml
├── docker-compose.yml
├── docs/
│   ├── Git工作流规范.md
│   ├── MIGRATION_GUIDE.md
│   ├── README.md
│   ├── api/
│   │   └── API_REFERENCE.md
│   ├── architecture/
│   │   ├── README.md
│   │   ├── README.zh-CN.md
│   │   ├── STRATEGY_MODULE.md
│   │   ├── STRATEGY_MODULE.zh-CN.md
│   │   └── X402_STREAMING_PAYMENT.md
│   ├── community/
│   │   ├── HOW_TO_MIGRATE_YOUR_PR.md
│   │   ├── HOW_TO_MIGRATE_YOUR_PR.zh-CN.md
│   │   ├── MIGRATION_ANNOUNCEMENT.md
│   │   ├── MIGRATION_ANNOUNCEMENT.zh-CN.md
│   │   ├── OFFICIAL_ACCOUNTS.md
│   │   ├── PR_COMMENT_TEMPLATE.md
│   │   ├── README.md
│   │   ├── bounty-aster.md
│   │   ├── bounty-guide.md
│   │   └── bounty-hyperliquid.md
│   ├── getting-started/
│   │   ├── README.md
│   │   ├── README.zh-CN.md
│   │   ├── aster-api-wallet.md
│   │   ├── binance-api.md
│   │   ├── blockrun-base-wallet.md
│   │   ├── blockrun-sol-wallet.md
│   │   ├── bybit-api.md
│   │   ├── custom-api.en.md
│   │   ├── custom-api.md
│   │   ├── hyperliquid-agent-wallet.md
│   │   ├── lighter-agent-wallet.md
│   │   └── okx-api.md
│   ├── guides/
│   │   ├── README.md
│   │   ├── README.zh-CN.md
│   │   ├── TROUBLESHOOTING.md
│   │   ├── TROUBLESHOOTING.zh-CN.md
│   │   ├── faq.en.md
│   │   └── faq.zh-CN.md
│   ├── i18n/
│   │   ├── README.md
│   │   ├── en/
│   │   │   ├── PRIVACY POLICY.md
│   │   │   └── TERMS OF SERVICE.md
│   │   ├── ja/
│   │   │   ├── PRIVACY POLICY.md
│   │   │   ├── README.md
│   │   │   └── TERMS OF SERVICE.md
│   │   ├── ko/
│   │   │   └── README.md
│   │   ├── ru/
│   │   │   ├── PRIVACY POLICY.md
│   │   │   ├── README.md
│   │   │   └── TERMS OF SERVICE.md
│   │   ├── uk/
│   │   │   ├── PRIVACY POLICY.md
│   │   │   ├── README.md
│   │   │   └── TERMS OF SERVICE.md
│   │   ├── vi/
│   │   │   └── README.md
│   │   └── zh-CN/
│   │       ├── CONTRIBUTING.md
│   │       ├── PRIVACY POLICY.md
│   │       ├── README.md
│   │       └── TERMS OF SERVICE.md
│   ├── legal/
│   │   ├── AGPL-VIOLATION-REPORT-ChainOpera-EN.md
│   │   └── AGPL-VIOLATION-REPORT-ChainOpera.md
│   ├── maintainers/
│   │   ├── PROJECT_MANAGEMENT.md
│   │   ├── PROJECT_MANAGEMENT.zh-CN.md
│   │   ├── PR_REVIEW_GUIDE.md
│   │   ├── PR_REVIEW_GUIDE.zh-CN.md
│   │   ├── README.md
│   │   ├── README.zh-CN.md
│   │   ├── SETUP_GUIDE.md
│   │   └── SETUP_GUIDE.zh-CN.md
│   ├── market-regime-classification-en.md
│   ├── market-regime-classification-zh.md
│   ├── plans/
│   │   ├── 2026-01-14-grid-trading-fixes.md
│   │   ├── 2026-01-17-grid-market-regime-design.md
│   │   ├── 2026-01-17-grid-market-regime-impl.md
│   │   ├── 2026-03-06-telegram-agent-redesign.md
│   │   └── 2026-03-06-telegram-bot.md
│   ├── pnl.md
│   ├── prompt-guide.md
│   ├── prompt-guide.zh-CN.md
│   ├── research/
│   │   └── AI-Trader-Analysis-Report.md
│   └── roadmap/
│       ├── README.md
│       └── README.zh-CN.md
├── go.mod
├── go.sum
├── hook/
│   ├── README.md
│   ├── hooks.go
│   ├── http_client_hook.go
│   ├── ip_hook.go
│   └── trader_hook.go
├── install-stable.sh
├── install.sh
├── kernel/
│   ├── engine.go
│   ├── engine_analysis.go
│   ├── engine_position.go
│   ├── engine_prompt.go
│   ├── formatter.go
│   ├── grid_engine.go
│   ├── prompt_builder.go
│   ├── prompt_builder_test.go
│   ├── schema.go
│   └── validate_test.go
├── logger/
│   ├── config.go
│   └── logger.go
├── main.go
├── manager/
│   └── trader_manager.go
├── market/
│   ├── api_client.go
│   ├── data.go
│   ├── data_indicators.go
│   ├── data_klines.go
│   ├── data_test.go
│   ├── historical.go
│   ├── timeframe.go
│   └── types.go
├── mcp/
│   ├── client.go
│   ├── client_test.go
│   ├── config.go
│   ├── config_usage_test.go
│   ├── context_guard.go
│   ├── context_guard_test.go
│   ├── examples_test.go
│   ├── hooks.go
│   ├── interface.go
│   ├── intro/
│   │   ├── BUILDER_EXAMPLES.md
│   │   ├── BUILDER_PATTERN_BENEFITS.md
│   │   ├── LOGRUS_INTEGRATION.md
│   │   ├── MIGRATION_GUIDE.md
│   │   └── README.md
│   ├── logger.go
│   ├── mock_test.go
│   ├── options.go
│   ├── options_test.go
│   ├── payment/
│   │   ├── blockrun_base.go
│   │   ├── blockrun_sol.go
│   │   ├── claw402.go
│   │   └── x402.go
│   ├── provider/
│   │   ├── claude.go
│   │   ├── deepseek.go
│   │   ├── gemini.go
│   │   ├── grok.go
│   │   ├── kimi.go
│   │   ├── minimax.go
│   │   ├── openai.go
│   │   ├── options_test.go
│   │   └── qwen.go
│   ├── providers.go
│   ├── registry.go
│   ├── request.go
│   ├── request_builder.go
│   └── request_builder_test.go
├── nginx/
│   └── nginx.conf
├── provider/
│   ├── alpaca/
│   │   └── kline.go
│   ├── coinank/
│   │   ├── base_coin.go
│   │   ├── coinank_api/
│   │   │   ├── base_coin.go
│   │   │   ├── base_coin_test.go
│   │   │   ├── depth_ws.go
│   │   │   ├── depth_ws_test.go
│   │   │   ├── kline.go
│   │   │   ├── kline_test.go
│   │   │   ├── kline_ws.go
│   │   │   └── kline_ws_test.go
│   │   ├── coinank_enum/
│   │   │   ├── exchange.go
│   │   │   ├── instrument_agg_sort_by.go
│   │   │   ├── interval.go
│   │   │   ├── product_type.go
│   │   │   ├── side.go
│   │   │   ├── sort_type.go
│   │   │   └── url.go
│   │   ├── coinank_http.go
│   │   ├── instrument_agg_rank.go
│   │   ├── instruments.go
│   │   ├── kline.go
│   │   ├── liquidation.go
│   │   ├── net_positions.go
│   │   └── open_interest.go
│   ├── hyperliquid/
│   │   ├── coins.go
│   │   ├── kline.go
│   │   └── kline_test.go
│   ├── nofxos/
│   │   ├── ai500.go
│   │   ├── client.go
│   │   ├── coin.go
│   │   ├── netflow.go
│   │   ├── oi.go
│   │   ├── price.go
│   │   └── util.go
│   └── twelvedata/
│       └── kline.go
├── railway/
│   └── start.sh
├── railway.toml
├── security/
│   └── url_validator.go
├── start.sh
├── store/
│   ├── ai_model.go
│   ├── decision.go
│   ├── driver.go
│   ├── equity.go
│   ├── exchange.go
│   ├── gorm.go
│   ├── grid.go
│   ├── order.go
│   ├── position.go
│   ├── position_builder.go
│   ├── position_history.go
│   ├── position_query.go
│   ├── store.go
│   ├── strategy.go
│   ├── telegram_config.go
│   ├── trader.go
│   └── user.go
├── telegram/
│   ├── agent/
│   │   ├── agent.go
│   │   ├── agent_test.go
│   │   ├── apicall.go
│   │   ├── manager.go
│   │   └── prompt.go
│   ├── bot.go
│   └── session/
│       └── memory.go
├── telemetry/
│   └── experience.go
├── trader/
│   ├── aster/
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   ├── trader_positions.go
│   │   ├── trader_sync.go
│   │   └── trader_test.go
│   ├── auto_trader.go
│   ├── auto_trader_decision.go
│   ├── auto_trader_grid.go
│   ├── auto_trader_grid_levels.go
│   ├── auto_trader_grid_orders.go
│   ├── auto_trader_grid_regime.go
│   ├── auto_trader_loop.go
│   ├── auto_trader_orders.go
│   ├── auto_trader_risk.go
│   ├── binance/
│   │   ├── futures.go
│   │   ├── futures_account.go
│   │   ├── futures_orders.go
│   │   ├── futures_positions.go
│   │   ├── futures_test.go
│   │   ├── order_sync.go
│   │   ├── order_sync_test.go
│   │   ├── sync_e2e_test.go
│   │   └── sync_verify_test.go
│   ├── bitget/
│   │   ├── order_sync.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_positions.go
│   ├── bybit/
│   │   ├── order_sync.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_positions.go
│   ├── exchange_sync_test.go
│   ├── gate/
│   │   ├── order_sync.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_test.go
│   ├── grid_regime.go
│   ├── grid_regime_test.go
│   ├── helpers.go
│   ├── hyperliquid/
│   │   ├── order_sync.go
│   │   ├── sync_test.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   ├── trader_positions.go
│   │   ├── trader_race_test.go
│   │   └── trader_sync.go
│   ├── indodax/
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_test.go
│   ├── interface.go
│   ├── kucoin/
│   │   ├── order_sync.go
│   │   ├── order_sync_test.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_positions.go
│   ├── lighter/
│   │   ├── account.go
│   │   ├── integration_test.go
│   │   ├── order_sync.go
│   │   ├── orders.go
│   │   ├── orders_test.go
│   │   ├── trader.go
│   │   ├── trading.go
│   │   └── types.go
│   ├── okx/
│   │   ├── order_sync.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_positions.go
│   ├── position_rebuild.go
│   ├── position_snapshot.go
│   ├── testutil/
│   │   └── test_suite.go
│   └── types/
│       └── interface.go
└── web/
    ├── .dockerignore
    ├── .husky/
    │   └── pre-commit
    ├── .prettierignore
    ├── .prettierrc.json
    ├── CHANGELOG.md
    ├── README.md
    ├── eslint.config.js
    ├── index.html
    ├── package.json
    ├── postcss.config.js
    ├── src/
    │   ├── App.tsx
    │   ├── components/
    │   │   ├── auth/
    │   │   │   ├── LoginPage.tsx
    │   │   │   ├── LoginRequiredOverlay.tsx
    │   │   │   ├── RegisterPage.test.tsx
    │   │   │   ├── RegisterPage.tsx
    │   │   │   ├── RegistrationDisabled.test.tsx
    │   │   │   ├── RegistrationDisabled.tsx
    │   │   │   └── ResetPasswordPage.tsx
    │   │   ├── charts/
    │   │   │   ├── AdvancedChart.tsx
    │   │   │   ├── ChartTabs.tsx
    │   │   │   ├── ChartWithOrders.tsx
    │   │   │   ├── ChartWithOrdersSimple.tsx
    │   │   │   ├── ComparisonChart.tsx
    │   │   │   ├── EquityChart.tsx
    │   │   │   └── TradingViewChart.tsx
    │   │   ├── common/
    │   │   │   ├── ConfirmDialog.tsx
    │   │   │   ├── Container.tsx
    │   │   │   ├── DeepVoidBackground.tsx
    │   │   │   ├── ExchangeIcons.tsx
    │   │   │   ├── Header.tsx
    │   │   │   ├── HeaderBar.tsx
    │   │   │   ├── MetricTooltip.tsx
    │   │   │   ├── ModelIcons.tsx
    │   │   │   ├── PunkAvatar.tsx
    │   │   │   ├── WebCryptoEnvironmentCheck.tsx
    │   │   │   └── WhitelistFullPage.tsx
    │   │   ├── faq/
    │   │   │   ├── FAQContent.tsx
    │   │   │   ├── FAQLayout.tsx
    │   │   │   ├── FAQSearchBar.tsx
    │   │   │   └── FAQSidebar.tsx
    │   │   ├── landing/
    │   │   │   ├── AboutSection.tsx
    │   │   │   ├── AnimatedSection.tsx
    │   │   │   ├── CommunitySection.tsx
    │   │   │   ├── FeaturesSection.tsx
    │   │   │   ├── FooterSection.tsx
    │   │   │   ├── HeroSection.tsx
    │   │   │   ├── HowItWorksSection.tsx
    │   │   │   ├── LoginModal.tsx
    │   │   │   ├── brand/
    │   │   │   │   ├── AgentTerminal.tsx
    │   │   │   │   ├── BrandFeatures.tsx
    │   │   │   │   ├── BrandHero.tsx
    │   │   │   │   ├── BrandStats.tsx
    │   │   │   │   └── Marquee.tsx
    │   │   │   └── core/
    │   │   │       ├── AgentGrid.tsx
    │   │   │       ├── DeploymentHub.tsx
    │   │   │       ├── LiveFeed.tsx
    │   │   │       └── TerminalHero.tsx
    │   │   ├── modals/
    │   │   │   ├── SetupPage.tsx
    │   │   │   └── TwoStageKeyModal.tsx
    │   │   ├── strategy/
    │   │   │   ├── CoinSourceEditor.tsx
    │   │   │   ├── GridConfigEditor.tsx
    │   │   │   ├── GridRiskPanel.tsx
    │   │   │   ├── IndicatorEditor.tsx
    │   │   │   ├── PromptSectionsEditor.tsx
    │   │   │   ├── PublishSettingsEditor.tsx
    │   │   │   └── RiskControlEditor.tsx
    │   │   ├── trader/
    │   │   │   ├── AITradersPage.tsx
    │   │   │   ├── CompetitionPage.test.tsx
    │   │   │   ├── CompetitionPage.tsx
    │   │   │   ├── ConfigStatusGrid.tsx
    │   │   │   ├── DecisionCard.tsx
    │   │   │   ├── ExchangeConfigModal.tsx
    │   │   │   ├── ModelCard.tsx
    │   │   │   ├── ModelConfigModal.tsx
    │   │   │   ├── ModelStepIndicator.tsx
    │   │   │   ├── PositionHistory.tsx
    │   │   │   ├── TelegramConfigModal.tsx
    │   │   │   ├── Tooltip.tsx
    │   │   │   ├── TraderConfigModal.tsx
    │   │   │   ├── TraderConfigViewModal.tsx
    │   │   │   ├── TradersList.tsx
    │   │   │   ├── model-constants.ts
    │   │   │   └── utils.ts
    │   │   └── ui/
    │   │       ├── alert-dialog.tsx
    │   │       └── input.tsx
    │   ├── constants/
    │   │   └── branding.ts
    │   ├── contexts/
    │   │   ├── AuthContext.tsx
    │   │   └── LanguageContext.tsx
    │   ├── hooks/
    │   │   ├── useCounterAnimation.ts
    │   │   ├── useGitHubStats.ts
    │   │   └── useSystemConfig.ts
    │   ├── i18n/
    │   │   ├── strategy-translations.ts
    │   │   └── translations.ts
    │   ├── index.css
    │   ├── lib/
    │   │   ├── api/
    │   │   │   ├── config.ts
    │   │   │   ├── data.ts
    │   │   │   ├── helpers.ts
    │   │   │   ├── index.ts
    │   │   │   ├── strategies.ts
    │   │   │   ├── telegram.ts
    │   │   │   └── traders.ts
    │   │   ├── apiGuard.test.ts
    │   │   ├── clipboard.ts
    │   │   ├── cn.ts
    │   │   ├── config.ts
    │   │   ├── crypto.ts
    │   │   ├── httpClient.ts
    │   │   ├── notify.tsx
    │   │   ├── registrationToggle.test.ts
    │   │   └── text.ts
    │   ├── main.tsx
    │   ├── pages/
    │   │   ├── DataPage.tsx
    │   │   ├── FAQPage.tsx
    │   │   ├── LandingPage.tsx
    │   │   ├── PageNotFound.tsx
    │   │   ├── SettingsPage.tsx
    │   │   ├── StrategyMarketPage.tsx
    │   │   ├── StrategyStudioPage.tsx
    │   │   └── TraderDashboardPage.tsx
    │   ├── stores/
    │   │   ├── index.ts
    │   │   ├── tradersConfigStore.ts
    │   │   └── tradersModalStore.ts
    │   ├── test/
    │   │   └── setup.ts
    │   ├── types/
    │   │   ├── config.ts
    │   │   ├── index.ts
    │   │   ├── strategy.ts
    │   │   └── trading.ts
    │   ├── utils/
    │   │   ├── format.ts
    │   │   ├── indicators.ts
    │   │   └── traderColors.ts
    │   └── vite-env.d.ts
    ├── tailwind.config.js
    ├── tsconfig.json
    ├── tsconfig.node.json
    ├── vite.config.ts
    └── vitest.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
# Git
.git
.gitignore
.github

# Docker
Dockerfile
docker-compose.yml
.dockerignore

# IDE
.idea
.vscode
*.swp
*.swo
*~

# Build artifacts
nofx
nofx_test
*.exe
*.dll
*.so
*.dylib

# Test files
*_test.go
test_*

# Documentation
*.md
!README.md
docs/

# Runtime data
decision_logs/
*.log

# Config files (should be mounted)
config.json

# Web build artifacts (but include source for multi-stage build)
web/node_modules/
web/dist/

# Temporary files
tmp/
temp/
*.tmp


================================================
FILE: .github/CLA.md
================================================
# NOFX Contributor License Agreement

Thank you for your interest in contributing to NOFX. This Contributor License Agreement ("CLA") documents the rights granted by contributors to the Project.

## 1. Definitions

- **"Contribution"** means any code, documentation, or other original work submitted to the Project.
- **"You"** means the individual or entity submitting the Contribution.
- **"Project"** means NOFX (https://github.com/NoFxAiOS/nofx).

## 2. Grant of Rights

By submitting a Contribution, you grant the Project a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license to:

- Use, copy, modify, and distribute your Contribution
- Sublicense your Contribution under the AGPL-3.0 license
- Create derivative works from your Contribution

## 3. Patent License

You grant the Project a perpetual, worldwide, non-exclusive, royalty-free, irrevocable patent license to make, use, sell, and distribute your Contribution.

## 4. Your Representations

You represent that:

- You have the legal right to grant this license
- Your Contribution is your original work
- Your Contribution does not violate any third-party rights
- If you are employed, you have permission from your employer to make this Contribution

## 5. No Warranty

Your Contribution is provided "AS IS" without any warranty of any kind.

## 6. Project License

All Contributions will be distributed under the **GNU Affero General Public License v3.0 (AGPL-3.0)**.

## 7. Applicable Law

This Agreement is governed by international copyright treaties including:

- Berne Convention
- TRIPS Agreement (WTO)
- WIPO Copyright Treaty (WCT)

---

By signing this CLA, you acknowledge that you have read and agree to these terms.

**Contact**: contact@vergex.trade


================================================
FILE: .github/CODEOWNERS
================================================
# CODEOWNERS
#
# This file defines code ownership and automatic reviewer assignment.
# When a PR touches files matching these patterns, the listed users/teams
# will be automatically requested for review.
#
# 此文件定义代码所有权和自动 reviewer 分配。
# 当 PR 涉及匹配这些模式的文件时,列出的用户/团队将自动被请求审查。
#
# Syntax | 语法:
#   pattern @username @org/team-name
#
# More specific patterns override less specific ones
# 更具体的模式会覆盖不太具体的模式
#
# Documentation: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners

# =============================================================================
# Global Owners | 全局所有者
# These users will be requested for review on ALL pull requests
# 这些用户将被请求审查所有 PR
# =============================================================================

* @NoFxAiOS @hzb1115 @tangmengqiu @mykelio1001 @Icyoung @SkywalkerJi

# =============================================================================
# Specific Component Owners | 特定组件所有者
# Additional reviewers based on file paths (in addition to global owners)
# 基于文件路径的额外 reviewers(在全局 owners 之外)
# =============================================================================

# Backend / Go Code | 后端 / Go 代码
# Go files and backend logic
*.go @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
go.mod @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
go.sum @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu


# Frontend / Web | 前端 / Web
# React/TypeScript frontend code
/web/ @0xEmberZz @hzb1115 @tangmengqiu
/web/src/ @0xEmberZz @hzb1115 @tangmengqiu
*.tsx @0xEmberZz @hzb1115 @tangmengqiu
*.ts @0xEmberZz @hzb1115 @tangmengqiu (frontend TypeScript only)
*.jsx @0xEmberZz @hzb1115 @tangmengqiu
*.css @0xEmberZz @hzb1115 @tangmengqiu
*.scss @0xEmberZz @hzb1115 @tangmengqiu

# Configuration Files | 配置文件
*.json @0xEmberZz @hzb1115 @tangmengqiu
*.yaml @0xEmberZz @hzb1115 @tangmengqiu
*.yml @0xEmberZz @hzb1115 @tangmengqiu
*.toml @0xEmberZz @hzb1115 @tangmengqiu
*.ini @0xEmberZz @hzb1115 @tangmengqiu

# Documentation | 文档
# Markdown and documentation files
*.md @hzb1115 @tangmengqiu
/docs/ @hzb1115 @tangmengqiu
README.md @hzb1115 @tangmengqiu

# GitHub Workflows & Actions | GitHub 工作流和 Actions
# CI/CD configuration and automation
/.github/ @hzb1115
/.github/workflows/ @hzb1115
/.github/workflows/*.yml @hzb1115

# Docker | Docker 配置
Dockerfile @tangmengqiu 
docker-compose.yml @tangmengqiu 
.dockerignore @tangmengqiu

# Database | 数据库
# Database migrations and schemas
/migrations/ @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
/db/ @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu
*.sql @SkywalkerJi @hzb1115 @Icyoung @tangmengqiu

# Scripts | 脚本
/scripts/ @hzb1115 @tangmengqiu
*.sh @hzb1115 @tangmengqiu
*.bash @hzb1115 @tangmengqiu
*.py @hzb1115 @tangmengqiu (if Python scripts exist)

# Tests | 测试
# Test files require review from component owners
*_test.go @SkywalkerJi @heronsbillC
/tests/ @SkywalkerJi @Icyoung @heronsbillC
/web/tests/ @Icyoung @hzb1115 @heronsbillC

# Security & Dependencies | 安全和依赖
# Security-sensitive files require extra attention
.env.example @hzb1115 @tangmengqiu
.gitignore @hzb1115 @tangmengqiu
go.sum @hzb1115 @tangmengqiu
package-lock.json @Icyoung @hzb1115 @tangmengqiu
yarn.lock @Icyoung @hzb1115 @tangmengqiu

# Build Configuration | 构建配置
Makefile @hzb1115 @tangmengqiu
/build/ @hzb1115 @tangmengqiu
/dist/ @hzb1115 @tangmengqiu

# License & Legal | 许可证和法律文件
LICENSE @hzb1115
COPYING @hzb1115

# =============================================================================
# Notes | 注意事项
# =============================================================================
#
# 1. All PRs will be assigned to the 5 global owners
#    所有 PR 都会分配给这 5 个全局 owners
#
# 2. Specific paths may add additional reviewers
#    特定路径可能会添加额外的 reviewers
#
# 3. PR author will NOT be requested for review (GitHub handles this)
#    PR 作者不会被请求审查(GitHub 自动处理)
#
# 4. You can adjust patterns and owners as needed
#    你可以根据需要调整模式和 owners
#
# 5. To require multiple approvals, configure branch protection rules
#    要求多个批准,请配置分支保护规则
#
# ⚠️ IMPORTANT - Permission Requirements | 重要 - 权限要求:
# - Users listed here will ONLY be auto-requested if they have Write+ permission
#   这里列出的用户只有在拥有 Write 或以上权限时才会被自动请求
# - GitHub will silently skip users without proper permissions
#   GitHub 会静默跳过没有适当权限的用户
# - See CODEOWNERS_PERMISSIONS.md for details
#   详见 CODEOWNERS_PERMISSIONS.md
#
# =============================================================================


================================================
FILE: .github/ISSUE_TEMPLATE/bounty_claim.md
================================================
---
name: Bounty Claim
about: Claim a bounty task or propose a new bounty
title: '[BOUNTY CLAIM] '
labels: bounty
assignees: ''
---

## 💰 Bounty Information

**Claiming existing bounty?**
- Issue #: <!-- Link to existing bounty issue -->
- Bounty amount: <!-- If specified -->

**OR proposing new bounty?**
- Proposed feature/fix: <!-- Brief description -->
- Estimated effort: [Small / Medium / Large]

---

## 👤 About You

**Name/Username:** <!-- Your name or GitHub username -->

**Contact:**
- GitHub: @your_username
- Telegram: @your_telegram (optional)
- Email: your@email.com (optional)

**Relevant Experience:**
- <!-- Link to your GitHub profile -->
- <!-- Previous contributions or similar projects -->
- <!-- Relevant skills (Go, React, trading systems, etc.) -->

---

## 📋 Implementation Plan

### 1. Approach
<!-- Describe your technical approach -->
- How will you implement this?
- What components will be affected?
- Any dependencies or libraries needed?

### 2. Timeline
- **Start date:** <!-- When you plan to start -->
- **Estimated completion:** <!-- How long will it take? -->
- **Milestones:**
  - [ ] Week 1: ...
  - [ ] Week 2: ...
  - [ ] Week 3: ...

### 3. Deliverables
- [ ] Working code (merged PR)
- [ ] Unit tests (if applicable)
- [ ] Documentation updates
- [ ] Demo video/screenshots

---

## 🔍 Questions for Maintainers

<!-- Any questions you have about the requirements? -->

1.
2.
3.

---

## 📚 References

<!-- Any relevant links, documentation, or examples -->

-

---

## ✅ Acknowledgment

By claiming this bounty, I acknowledge that:
- [ ] I have read the [Contributing Guide](../../CONTRIBUTING.md)
- [ ] I will follow the [Code of Conduct](../../CODE_OF_CONDUCT.md)
- [ ] I understand the acceptance criteria
- [ ] My contribution will be licensed under AGPL-3.0 License
- [ ] Payment is subject to successful PR merge

---

**For maintainers:**
- [ ] Bounty claim approved
- [ ] Issue assigned to claimant
- [ ] Timeline agreed upon


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report
about: Report a bug to help us improve NOFX
title: '[BUG] '
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 '...' / Run command '...'
3. Configure '...'
4. See error

## ✅ Expected Behavior
<!-- What you expected to happen -->


## ❌ Actual Behavior
<!-- What actually happened -->


## 📸 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
```

**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]
- **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.]

## 🔧 Configuration (if relevant)
<!-- Only include non-sensitive parts of your config -->
<!-- ⚠️ NEVER paste API keys or private keys! -->

**Leverage Settings:**
```json
{
  "btc_eth_leverage": 5,
  "altcoin_leverage": 5
}
```

**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`


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature Request
about: Suggest a new feature for NOFX
title: '[FEATURE] '
labels: enhancement
assignees: ''
---

## 📋 Feature Description
<!-- A clear and concise description of what you want to happen -->

## 🎯 Problem to Solve
<!-- What problem does this feature solve? -->

## 💡 Proposed Solution
<!-- How should this feature work? -->

## 🔧 Technical Details
<!-- Any technical considerations or implementation ideas -->

## ✅ Acceptance Criteria
<!-- What needs to be done for this feature to be considered complete? -->
- [ ] Item 1
- [ ] Item 2

## 📚 Additional Context
<!-- Add any other context, screenshots, or references about the feature request here -->


================================================
FILE: .github/PR_TITLE_GUIDE.md
================================================
# PR Title Guide

## 📋 Overview

We use the **Conventional Commits** format to maintain consistency in PR titles, but this is **recommended**, not mandatory. It will not prevent your PR from being merged.

## ✅ Recommended Format

```
type(scope): description
```

### Examples

```
feat(trader): add new trading strategy
fix(api): resolve authentication issue
docs: update README
chore(deps): update dependencies
ci(workflow): improve GitHub Actions
```

---

## 📖 Detailed Guide

### Type - Required

Describes the type of change:

| Type | Description | Example |
|------|-------------|---------|
| `feat` | New feature | `feat(trader): add stop-loss feature` |
| `fix` | Bug fix | `fix(api): handle null response` |
| `docs` | Documentation change | `docs: update installation guide` |
| `style` | Code formatting (no functional change) | `style: format code with prettier` |
| `refactor` | Code refactoring (neither feature nor fix) | `refactor(exchange): simplify connection logic` |
| `perf` | Performance optimization | `perf(ai): optimize prompt processing` |
| `test` | Add or modify tests | `test(trader): add unit tests` |
| `chore` | Build process or auxiliary tool changes | `chore: update dependencies` |
| `ci` | CI/CD related changes | `ci: add test coverage report` |
| `security` | Security fixes | `security: update vulnerable dependencies` |
| `build` | Build system or external dependency changes | `build: upgrade webpack to v5` |

### Scope - Optional

Describes the area affected by the change:

| Scope | Description |
|-------|-------------|
| `exchange` | Exchange-related |
| `trader` | Trader/trading strategy |
| `ai` | AI model related |
| `api` | API interface |
| `ui` | User interface |
| `frontend` | Frontend code |
| `backend` | Backend code |
| `security` | Security related |
| `deps` | Dependencies |
| `workflow` | GitHub Actions workflows |
| `github` | GitHub configuration |
| `actions` | GitHub Actions |
| `config` | Configuration files |
| `docker` | Docker related |
| `build` | Build related |
| `release` | Release related |

**Note:** If the change affects multiple scopes, you can omit the scope or choose the most relevant one.

### Description - Required

- Use present tense ("add" not "added")
- Start with lowercase
- No period at the end
- Concisely describe what changed

---

## 🎯 Complete Examples

### ✅ Good PR Titles

```
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
```

### ⚠️ Titles That Need Improvement

| Poor Title | Issue | Improved |
|-----------|-------|----------|
| `update code` | Too vague | `refactor(trader): simplify order execution logic` |
| `Fixed bug` | Capitalized, not specific | `fix(api): handle edge case in login` |
| `Add new feature.` | Has period, not specific | `feat(ui): add dark mode toggle` |
| `changes` | Doesn't follow format | `chore: update dependencies` |
| `feat: Added new trading algo` | Wrong tense | `feat(trader): add new trading algorithm` |

---

## 🤖 Automated Check Behavior

### When PR Title Doesn't Follow Format

1. **Won't block merging** ✅
   - Check is marked as "advisory"
   - PR can still be reviewed and merged

2. **Provides friendly reminder** 💬
   - Bot will comment on the PR
   - Provides format guidance and examples
   - Suggests how to improve the title

3. **Can be updated anytime** 🔄
   - Re-checks after updating PR title
   - No need to close and reopen PR

### Example Comment

If your PR title is `update workflow`, you'll receive a comment like this:

```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.
```

---

## 🔧 Configuration Details

### Supported Types

Configured in `.github/workflows/pr-checks.yml`:

```yaml
types: |
  feat
  fix
  docs
  style
  refactor
  perf
  test
  chore
  ci
  security
  build
```

### Supported Scopes

```yaml
scopes: |
  exchange
  trader
  ai
  api
  ui
  frontend
  backend
  security
  deps
  workflow
  github
  actions
  config
  docker
  build
  release
```

### Adding New Scopes

If you need to add a new scope:

1. Add it to the `scopes` section in `.github/workflows/pr-checks.yml`
2. Update the regex in `.github/workflows/pr-checks-run.yml` (optional)
3. Update this documentation

---

## 📚 Why Use Conventional Commits?

### Benefits

1. **Automated Changelog** 📝
   - Automatically generate version changelogs
   - Clearly categorize different types of changes

2. **Semantic Versioning** 🔢
   - `feat` → MINOR version (1.1.0)
   - `fix` → PATCH version (1.0.1)
   - `BREAKING CHANGE` → MAJOR version (2.0.0)

3. **Better Readability** 👀
   - Understand PR purpose at a glance
   - Easier to browse Git history

4. **Team Collaboration** 🤝
   - Unified commit style
   - Reduces communication overhead

### Example: Auto-generated 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)
```

---

## 🎓 Learning Resources

- **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: Must I follow this format?

**A:** No. This is recommended but not mandatory. It won't block your PR from being merged. However, following the format improves project maintainability.

### Q: What if I forget?

**A:** The bot will remind you in the PR comments. You can update the title anytime.

### Q: Can I make multiple types of changes in one PR?

**A:** Yes, but it's recommended to:
- Choose the most significant type
- Or consider splitting into multiple PRs (easier to review)

### Q: Can I omit the scope?

**A:** Yes. `requireScope: false` means scope is optional.

Example: `docs: update README` (no scope is fine)

### Q: How do I add a new type or scope?

**A:** Submit a PR to modify `.github/workflows/pr-checks.yml` and document the purpose of the new item in this guide.

### Q: How do I indicate Breaking Changes?

**A:** Add `BREAKING CHANGE:` in the description or add `!` after the type:

```
feat!: remove deprecated API
feat(api)!: change authentication method

BREAKING CHANGE: The old /auth endpoint is removed
```

---

## 📊 Statistics

Want to see the commit type distribution in your project? Run:

```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
```

---

## ✅ Quick Checklist

Before submitting a PR, check if your title:

- [ ] Contains a valid type (feat, fix, docs, etc.)
- [ ] Starts with lowercase
- [ ] Uses present tense ("add" not "added")
- [ ] Is concise (preferably under 50 characters)
- [ ] Accurately describes the change

**Remember:** These are recommendations, not requirements!


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/README.md
================================================
# PR Templates

## 📋 Template Overview

We offer 4 specialized templates for different types of PRs to help contributors quickly fill out PR information:

### 1. 🔧 Backend Template
**File:** `backend.md`

**Use for:**
- Go code changes
- API endpoint development
- Trading logic implementation
- Backend performance optimization
- Database-related changes

**Includes:**
- Go test environment
- Security considerations
- Performance impact assessment
- `go fmt` and `go build` checks

### 2. 🎨 Frontend Template
**File:** `frontend.md`

**Use for:**
- UI/UX changes
- React/Vue component development
- Frontend styling updates
- Browser compatibility fixes
- Frontend performance optimization

**Includes:**
- Screenshots/demo requirements
- Browser testing checklist
- Internationalization checks
- Responsive design verification
- `npm run lint` and `npm run build` checks

### 3. 📝 Documentation Template
**File:** `docs.md`

**Use for:**
- README updates
- API documentation
- Tutorials and guides
- Code comment improvements
- Translation work

**Includes:**
- Documentation type classification
- Content quality checks
- Bilingual requirements (EN/CN)
- Link validity verification

### 4. 📦 General Template
**File:** `general.md`

**Use for:**
- Mixed-type changes
- Cross-domain PRs
- Build configuration changes
- Dependency updates
- When unsure which template to use

## 🤖 Automatic Template Suggestion

Our GitHub Action automatically analyzes your PR and suggests the most suitable template:

### How it works:

1. **File Analysis**
   - Detects all changed file types in the PR

2. **Smart Detection**
   - If >50% are `.go` files → Suggests **Backend template**
   - If >50% are `.js/.ts/.tsx/.vue` files → Suggests **Frontend template**
   - If >70% are `.md` files → Suggests **Documentation template**

3. **Auto-comment**
   - If it detects you're using the default template but should use a specialized one
   - It will automatically add a friendly comment suggestion

4. **Auto-labeling**
   - Automatically adds corresponding labels: `backend`, `frontend`, `documentation`

## 📖 How to Use

### Method 1: URL Parameter (Recommended)

When creating a PR, add the template parameter to the URL:

```
https://github.com/YOUR_ORG/nofx/compare/dev...YOUR_BRANCH?template=backend.md
```

Replace `backend.md` with:
- `backend.md` - Backend template
- `frontend.md` - Frontend template
- `docs.md` - Documentation template
- `general.md` - General template

### Method 2: Manual Selection

1. When creating a PR, the default template will be shown

2. Follow the guidance links at the top to view the corresponding template

3. Copy the template content into the PR description

### Method 3: Follow Auto-suggestion

1. Create a PR with any template

2. GitHub Action will automatically analyze and comment with a suggestion

3. Update the PR description based on the suggestion

## 🎯 Best Practices

1. **Choose in Advance**
   - Determine the change type before creating the PR

2. **Complete Filling**
   - Don't skip required items

3. **Keep it Concise**
   - Keep descriptions clear but concise

4. **Add Screenshots**
   - For UI changes, always add screenshots

5. **Test Evidence**
   - Provide evidence that tests pass

## 🔧 Customization

If you need to modify templates or auto-detection logic:

1. **Modify Templates**
   - Edit `.github/PULL_REQUEST_TEMPLATE/*.md` files

2. **Adjust Detection Threshold**
   - Edit `.github/workflows/pr-template-suggester.yml`
   - Modify file type percentage thresholds (current: 50% backend, 50% frontend, 70% docs)

3. **Add New Template**
   - Create a new `.md` file in the `PULL_REQUEST_TEMPLATE/` directory
   - Update the workflow to support new file type detection

## ❓ FAQ

**Q: My PR has both frontend and backend code, which template should I use?**

A: Use the **General template** (`general.md`), or choose the template for the primary change type.

---

**Q: What if the automatically suggested template is not suitable?**

A: You can ignore the suggestion and continue using the current template. Auto-suggestions are for reference only.

---

**Q: Can I not use any template?**

A: Not recommended. Templates help ensure PRs contain necessary information and speed up reviews.

---

**Q: How to disable automatic template suggestions?**

A: Delete or disable the `.github/workflows/pr-template-suggester.yml` file.

---

🌟 **Thank you for using our PR template system!**


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/backend.md
================================================
# Pull Request - Backend

> **💡 Tip:** Recommended PR title format `type(scope): description`
> Example: `feat(trader): add new strategy` | `fix(api): resolve auth issue`

---

## 📝 Description



---

## 🎯 Type of Change

- [ ] 🐛 Bug fix
- [ ] ✨ New feature
- [ ] 💥 Breaking change
- [ ] ♻️ Refactoring
- [ ] ⚡ Performance improvement
- [ ] 🔒 Security fix
- [ ] 🔧 Build/config change

---

## 🔗 Related Issues

- Closes #
- Related to #

---

## 📋 Changes Made

-
-

---

## 🧪 Testing

### Test Environment
- **OS:**
- **Go Version:**
- **Exchange:** [if applicable]

### Manual Testing
- [ ] Tested locally
- [ ] Tested on testnet (for exchange integration)
- [ ] Unit tests pass
- [ ] Verified no existing functionality broke

### Test Results
```
Test output here
```

---

## 🔒 Security Considerations

- [ ] No API keys or secrets hardcoded
- [ ] User inputs properly validated
- [ ] No SQL injection vulnerabilities
- [ ] Authentication/authorization properly handled
- [ ] Sensitive data is encrypted
- [ ] N/A (not security-related)

---

## ⚡ Performance Impact

- [ ] No significant performance impact
- [ ] Performance improved
- [ ] Performance may be impacted (explain below)

**If impacted, explain:**


---

## ✅ Checklist

### Code Quality
- [ ] Code follows project style
- [ ] Self-review completed
- [ ] Comments added for complex logic
- [ ] Code compiles successfully (`go build`)
- [ ] Ran `go fmt`

### Documentation
- [ ] Updated relevant documentation
- [ ] Added inline comments where necessary
- [ ] Updated API documentation (if applicable)

### Git
- [ ] Commits follow conventional format
- [ ] Rebased on latest `dev` branch
- [ ] No merge conflicts

---

## 📚 Additional Notes


---

**By submitting this PR, I confirm:**

- [ ] I have read the [Contributing Guidelines](../../CONTRIBUTING.md)
- [ ] I agree to the [Code of Conduct](../../CODE_OF_CONDUCT.md)
- [ ] My contribution is licensed under AGPL-3.0

---

🌟 **Thank you for your contribution!**


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/docs.md
================================================
# Pull Request - Documentation

> **💡 Tip:** Recommended PR title format `docs(scope): description`
> Example: `docs(api): update trading endpoints` | `docs(readme): add setup guide`

---

## 📝 Description


---

## 📚 Type of Documentation

- [ ] 📖 README update
- [ ] 📋 API documentation
- [ ] 🎓 Tutorial/Guide
- [ ] 📝 Code comments
- [ ] 🔧 Configuration docs
- [ ] 🐛 Fix typo/error
- [ ] 🌍 Translation

---

## 🔗 Related Issues

- Closes #
- Related to #

---

## 📋 Changes Made

-
-

---

## 📸 Screenshots (if applicable)

<!-- For documentation with images, diagrams, or UI examples -->


---

## 🌐 Internationalization

- [ ] English version complete
- [ ] Chinese version complete
- [ ] Both versions are consistent
- [ ] N/A (only one language needed)

---

## ✅ Checklist

### Content Quality
- [ ] Information is accurate and up-to-date
- [ ] Language is clear and concise
- [ ] No spelling or grammar errors
- [ ] Links are valid and working
- [ ] Code examples are tested and working
- [ ] Formatting is consistent

### Documentation Standards
- [ ] Follows project documentation style
- [ ] Includes necessary examples
- [ ] Technical terms are explained
- [ ] Self-review completed

### Git
- [ ] Commits follow conventional format
- [ ] Rebased on latest `dev` branch
- [ ] No merge conflicts

---

## 📚 Additional Notes


---

**By submitting this PR, I confirm:**

- [ ] I have read the [Contributing Guidelines](../../CONTRIBUTING.md)
- [ ] I agree to the [Code of Conduct](../../CODE_OF_CONDUCT.md)
- [ ] My contribution is licensed under AGPL-3.0

---

🌟 **Thank you for your contribution!**


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/frontend.md
================================================
# Pull Request - Frontend

> **💡 Tip:** Recommended PR title format `type(scope): description`
> Example: `feat(ui): add dark mode toggle` | `fix(form): resolve validation bug`

---

## 📝 Description


---

## 🎯 Type of Change

- [ ] 🐛 Bug fix
- [ ] ✨ New feature
- [ ] 💥 Breaking change
- [ ] 🎨 Code style update
- [ ] ♻️ Refactoring
- [ ] ⚡ Performance improvement

---

## 🔗 Related Issues

- Closes #
- Related to #

---

## 📋 Changes Made

-
-

---

## 📸 Screenshots / Demo

<!-- For UI changes, include before/after screenshots or video demo -->

**Before:**


**After:**


---

## 🧪 Testing

### Test Environment
- **OS:**
- **Node Version:**
- **Browser(s):**

### Manual Testing
- [ ] Tested in development mode
- [ ] Tested production build
- [ ] Tested on multiple browsers
- [ ] Tested responsive design
- [ ] Verified no existing functionality broke

---

## 🌐 Internationalization

- [ ] All user-facing text supports i18n
- [ ] Both English and Chinese versions provided
- [ ] N/A

---

## ✅ Checklist

### Code Quality
- [ ] Code follows project style
- [ ] Self-review completed
- [ ] Comments added for complex logic
- [ ] Code builds successfully (`npm run build`)
- [ ] Ran `npm run lint`
- [ ] No console errors or warnings

### Testing
- [ ] Component tests added/updated
- [ ] Tests pass locally

### Documentation
- [ ] Updated relevant documentation
- [ ] Updated type definitions (TypeScript)
- [ ] Added JSDoc comments where necessary

### Git
- [ ] Commits follow conventional format
- [ ] Rebased on latest `dev` branch
- [ ] No merge conflicts

---

## 📚 Additional Notes


---

**By submitting this PR, I confirm:**

- [ ] I have read the [Contributing Guidelines](../../CONTRIBUTING.md)
- [ ] I agree to the [Code of Conduct](../../CODE_OF_CONDUCT.md)
- [ ] My contribution is licensed under AGPL-3.0

---

🌟 **Thank you for your contribution!**


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/general.md
================================================
# Pull Request - General

> **💡 Tip:** Recommended PR title format `type(scope): description`
> Example: `feat(trader): add new strategy` | `fix(api): resolve auth issue` | `docs(readme): update`

---

## 📝 Description


---

## 🎯 Type of Change

- [ ] 🐛 Bug fix
- [ ] ✨ New feature
- [ ] 💥 Breaking change
- [ ] 📝 Documentation update
- [ ] 🎨 Code style update
- [ ] ♻️ Refactoring
- [ ] ⚡ Performance improvement
- [ ] ✅ Test update
- [ ] 🔧 Build/config change
- [ ] 🔒 Security fix

---

## 🔗 Related Issues

- Closes #
- Related to #

---

## 📋 Changes Made

-
-

---

## 🧪 Testing

- [ ] Tested locally
- [ ] Tests pass
- [ ] Verified no existing functionality broke

**Test details:**


---

## ✅ Checklist

### Code Quality
- [ ] Code follows project style
- [ ] Self-review completed
- [ ] Comments added for complex logic
- [ ] No new warnings or errors

### Documentation
- [ ] Updated relevant documentation
- [ ] Added inline comments where necessary

### Git
- [ ] Commits follow conventional format
- [ ] Rebased on latest `dev` branch
- [ ] No merge conflicts

---

## 🔒 Security (if applicable)

- [ ] No API keys or secrets hardcoded
- [ ] User inputs properly validated
- [ ] N/A

---

## 📚 Additional Notes


---

**By submitting this PR, I confirm:**

- [ ] I have read the [Contributing Guidelines](../../CONTRIBUTING.md)
- [ ] I agree to the [Code of Conduct](../../CODE_OF_CONDUCT.md)
- [ ] My contribution is licensed under AGPL-3.0

---

🌟 **Thank you for your contribution!**


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Summary

- Problem:
- What changed:
- What did NOT change (scope boundary):

## Change Type

- [ ] Bug fix
- [ ] Feature
- [ ] Refactoring
- [ ] Docs
- [ ] Security fix
- [ ] Chore / infra

## Scope

- [ ] Trading engine / strategies
- [ ] MCP / AI clients
- [ ] API / server
- [ ] Telegram bot / agent
- [ ] Web UI / frontend
- [ ] Config / deployment
- [ ] CI/CD / infra

## Linked Issues

- Closes #
- Related #

## Testing

What you verified and how:

- [ ] `go build ./...` passes
- [ ] `go test ./...` passes
- [ ] Manual testing done (describe below)

## Security Impact

- Secrets/keys handling changed? (`Yes/No`)
- New/changed API endpoints? (`Yes/No`)
- User input validation affected? (`Yes/No`)

## Compatibility

- Backward compatible? (`Yes/No`)
- Config/env changes? (`Yes/No`)
- Migration needed? (`Yes/No`)
- If yes, upgrade steps:


================================================
FILE: .github/SECURITY.md
================================================
# Security Policy

## 🔒 Security at NOFX

We take the security of NOFX seriously. This document outlines our security policy and procedures for reporting vulnerabilities.

## 📋 Supported Versions

We release patches for security vulnerabilities. Which versions are eligible for receiving such patches depends on the CVSS v3.0 Rating:

| Version | Supported          | Status |
| ------- | ------------------ | ------ |
| 3.x.x   | ✅ Yes             | Active development |
| 2.x.x   | ⚠️ Limited support | Security fixes only |
| < 2.0   | ❌ No              | No longer supported |

## 🚨 Reporting a Vulnerability

**Please do not report security vulnerabilities through public GitHub issues.**

If you discover a security vulnerability, please follow these steps:

### 1. Private Disclosure

Send an email to the security team at:
- **Email**: tinklefund@gmail.com (or contact maintainers directly via Twitter DM)
- **Twitter**: [@nofx_official](https://x.com/nofx_official) or [@Web3Tinkle](https://x.com/Web3Tinkle)

### 2. Information to Include

Please include the following details in your report:

- **Description**: A clear description of the vulnerability
- **Impact**: The potential impact of the vulnerability
- **Steps to Reproduce**: Detailed steps to reproduce the issue
- **Proof of Concept**: If applicable, include PoC code or screenshots
- **Suggested Fix**: If you have ideas on how to fix it
- **Your Contact Information**: For follow-up questions

### 3. Response Timeline

- **Initial Response**: Within 48 hours of receiving your report
- **Status Update**: Weekly updates on the progress
- **Fix Timeline**: Critical issues within 7 days, others within 30 days
- **Public Disclosure**: After the fix is deployed (coordinated disclosure)

### 4. What to Expect

After you submit a report:

1. ✅ We will acknowledge receipt of your report
2. 🔍 We will investigate and validate the issue
3. 📋 We will develop and test a fix
4. 🚀 We will deploy the fix to production
5. 📢 We will coordinate public disclosure with you
6. 🏆 We will credit you in the security advisory (if desired)

## 🛡️ Security Best Practices

If you're using NOFX, please follow these security best practices:

### API Keys and Secrets

- ❌ **Never commit** API keys, private keys, or secrets to version control
- ✅ **Use environment variables** for all sensitive configuration
- ✅ **Rotate keys regularly** (at least every 90 days)
- ✅ **Use separate keys** for different environments (dev/staging/prod)
- ✅ **Implement IP whitelisting** for exchange API keys
- ✅ **Enable 2FA** on all exchange accounts

### Private Keys (Hyperliquid/Aster)

- ❌ **Never share** your private keys with anyone
- ✅ **Use dedicated wallets** for trading (not your main wallet)
- ✅ **Use agent wallets** when available (Hyperliquid)
- ✅ **Limit wallet funds** to amounts you can afford to lose
- ✅ **Back up keys securely** using encrypted storage

### API Security

- ✅ **Enable API key restrictions** (IP whitelist, permissions)
- ✅ **Use read-only keys** for monitoring when possible
- ✅ **Set withdrawal restrictions** on exchange accounts
- ✅ **Monitor API usage** for unusual activity
- ✅ **Revoke compromised keys** immediately

### System Security

- ✅ **Keep dependencies updated** (run `npm audit` and `go mod tidy`)
- ✅ **Use HTTPS** for all external communications
- ✅ **Implement rate limiting** on API endpoints
- ✅ **Enable authentication** on production deployments
- ✅ **Review logs regularly** for suspicious activity
- ✅ **Use Docker** for isolated environments

### Database Security

- ✅ **Encrypt sensitive data** at rest (API keys, private keys)
- ✅ **Restrict database access** (not exposed to internet)
- ✅ **Back up regularly** with encrypted backups
- ✅ **Use strong passwords** for database credentials

### Configuration Security

- ❌ **Never use default passwords** or weak credentials
- ✅ **Change default ports** if exposed to internet
- ✅ **Disable unnecessary features** in production
- ✅ **Use firewall rules** to restrict access
- ✅ **Implement RBAC** for multi-user setups

## 🚫 Out of Scope

The following are **not** considered security vulnerabilities:

- ❌ Trading losses due to AI decisions
- ❌ Exchange API rate limiting
- ❌ Network latency issues
- ❌ Market volatility impacts
- ❌ Social engineering attacks
- ❌ DDoS attacks on public infrastructure
- ❌ Issues in third-party dependencies (report to upstream)
- ❌ Already known and documented limitations

## 🏅 Recognition

We appreciate the security research community's efforts. Contributors who responsibly disclose vulnerabilities will be:

- ✅ Credited in security advisories (with permission)
- ✅ Listed in our Hall of Fame (coming soon)
- ✅ Eligible for bug bounties (when program launches)

## 📚 Security Resources

### Documentation

- [Getting Started Guide](../docs/getting-started/README.md)
- [Architecture Documentation](../docs/architecture/README.md)
- [Docker Deployment Guide](../docs/getting-started/docker-deploy.en.md)
- [Troubleshooting Guide](../docs/guides/TROUBLESHOOTING.md)

### Security Tools

- **Code Scanning**: GitHub Advanced Security (enabled)
- **Dependency Scanning**: Dependabot (enabled)
- **Secret Scanning**: GitHub Secret Scanning (enabled)
- **Container Scanning**: Docker Scout (recommended)

### External Resources

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [CWE Top 25](https://cwe.mitre.org/top25/archive/2023/2023_top25_list.html)
- [NIST Cybersecurity Framework](https://www.nist.gov/cyberframework)

## 🔐 Encryption & Secure Storage

NOFX uses the following security measures:

- **AES-256 encryption** for sensitive data at rest (planned v3.1)
- **TLS 1.3** for all network communications
- **JWT tokens** for API authentication
- **bcrypt** for password hashing (where applicable)
- **Environment isolation** via Docker containers

## 📝 Security Audit History

| Date | Version | Auditor | Report |
|------|---------|---------|--------|
| TBD  | 3.0.0   | Internal | Initial security review |

## 🤝 Responsible Disclosure Policy

We follow a **coordinated disclosure** approach:

1. 📧 Report received and acknowledged
2. 🔍 Investigation and validation (1-7 days)
3. 🛠️ Fix development and testing (7-30 days)
4. 🚀 Fix deployment to production
5. 📢 Public advisory published (after fix)
6. 🏆 Credit to researcher (if desired)

**Please allow us time to fix critical issues before public disclosure.**

## 📞 Contact

For security concerns, reach out via:

- **Email**: Contact maintainers (see [GitHub profile](https://github.com/NoFxAiOS/nofx))
- **Twitter**: [@nofx_official](https://x.com/nofx_official) (DM open)
- **Telegram**: [NOFX Developer Community](https://t.me/nofx_dev_community)
- **GitHub**: Private security advisory (preferred for verified issues)

## ⚖️ Legal

**Safe Harbor**: We consider security research conducted under this policy to be:

- ✅ Authorized in accordance with applicable law
- ✅ Lawful and in good faith
- ✅ Exempt from DMCA and CFAA claims
- ✅ Protected from legal action by the project

**Conditions**:
- Make a good faith effort to avoid privacy violations
- Do not access or modify other users' data
- Do not disrupt our services or infrastructure
- Do not publicly disclose issues before we've had time to address them

## 🔄 Updates to This Policy

This security policy may be updated from time to time. We will notify users of significant changes via:

- GitHub release notes
- Security advisories
- Community channels (Telegram, Twitter)

---

**Last Updated**: January 2025
**Version**: 1.0.0

Thank you for helping keep NOFX and its users safe! 🙏

---

## 📖 Additional Resources

- [Contributing Guidelines](../CONTRIBUTING.md)
- [Code of Conduct](../CODE_OF_CONDUCT.md)
- [License](../LICENSE)
- [Changelog](../CHANGELOG.md)


================================================
FILE: .github/labeler.yml
================================================
# Auto-labeler configuration
# Automatically adds labels based on changed files

# Area: Frontend
'area: frontend':
  - changed-files:
    - any-glob-to-any-file:
      - 'web/**/*'
      - '*.tsx'
      - '*.ts'
      - '*.jsx'
      - '*.js'
      - '*.css'

# Area: Backend
'area: backend':
  - changed-files:
    - any-glob-to-any-file:
      - '**/*.go'
      - 'go.mod'
      - 'go.sum'
      - 'cmd/**/*'
      - 'internal/**/*'
      - 'pkg/**/*'

# Area: Exchange
'area: exchange':
  - changed-files:
    - any-glob-to-any-file:
      - 'internal/exchange/**/*'
      - 'pkg/exchange/**/*'
      - '**/binance*.go'
      - '**/hyperliquid*.go'
      - '**/aster*.go'
      - '**/okx*.go'
      - '**/bybit*.go'

# Area: AI
'area: ai':
  - changed-files:
    - any-glob-to-any-file:
      - 'internal/ai/**/*'
      - 'pkg/ai/**/*'
      - '**/deepseek*.go'
      - '**/qwen*.go'
      - '**/openai*.go'
      - '**/claude*.go'

# Area: API
'area: api':
  - changed-files:
    - any-glob-to-any-file:
      - 'internal/api/**/*'
      - 'pkg/api/**/*'
      - '**/handler*.go'
      - '**/router*.go'

# Area: Security
'area: security':
  - changed-files:
    - any-glob-to-any-file:
      - '**/auth*.go'
      - '**/jwt*.go'
      - '**/encryption*.go'
      - '**/crypto*.go'
      - 'SECURITY.md'

# Area: Database
'area: database':
  - changed-files:
    - any-glob-to-any-file:
      - 'internal/database/**/*'
      - 'internal/db/**/*'
      - '**/migration*.go'
      - '**/*.sql'
      - '**/schema*.go'

# Area: UI/UX
'area: ui/ux':
  - changed-files:
    - any-glob-to-any-file:
      - 'web/src/components/**/*'
      - 'web/src/pages/**/*'
      - '**/*.css'
      - '**/style*.ts'

# Area: Deployment
'area: deployment':
  - changed-files:
    - any-glob-to-any-file:
      - 'Dockerfile'
      - 'docker-compose*.yml'
      - '.github/workflows/**/*'
      - 'start.sh'
      - '**/*deploy*.md'

# Type: Documentation
'type: documentation':
  - changed-files:
    - any-glob-to-any-file:
      - 'docs/**/*'
      - '*.md'
      - 'README*'
      - 'CHANGELOG*'
      - 'CONTRIBUTING.md'
      - 'CODE_OF_CONDUCT.md'

# Type: Test
'type: test':
  - changed-files:
    - any-glob-to-any-file:
      - '**/*_test.go'
      - 'test/**/*'
      - '**/*.test.ts'
      - '**/*.test.tsx'
      - '**/*.spec.ts'

# Dependencies
'dependencies':
  - changed-files:
    - any-glob-to-any-file:
      - 'go.mod'
      - 'go.sum'
      - 'package.json'
      - 'package-lock.json'
      - 'web/package.json'
      - 'web/package-lock.json'


================================================
FILE: .github/labels.yml
================================================
# GitHub Labels Configuration
# Use https://github.com/crazy-max/ghaction-github-labeler to sync labels

# Priority Labels
- name: "priority: critical"
  color: "d73a4a"
  description: "Critical priority - requires immediate attention"

- name: "priority: high"
  color: "ff6b6b"
  description: "High priority - should be addressed soon"

- name: "priority: medium"
  color: "fbca04"
  description: "Medium priority - normal queue"

- name: "priority: low"
  color: "0e8a16"
  description: "Low priority - nice to have"

# Type Labels
- name: "type: bug"
  color: "d73a4a"
  description: "Something isn't working"

- name: "type: feature"
  color: "a2eeef"
  description: "New feature or request"

- name: "type: enhancement"
  color: "84b6eb"
  description: "Improvement to existing feature"

- name: "type: documentation"
  color: "0075ca"
  description: "Documentation improvements"

- name: "type: security"
  color: "ee0701"
  description: "Security-related changes"

- name: "type: performance"
  color: "f9d0c4"
  description: "Performance improvements"

- name: "type: refactor"
  color: "fbca04"
  description: "Code refactoring"

- name: "type: test"
  color: "c5def5"
  description: "Test-related changes"

# Status Labels
- name: "status: needs review"
  color: "fbca04"
  description: "PR is ready for review"

- name: "status: needs changes"
  color: "d93f0b"
  description: "PR needs changes based on review"

- name: "status: on hold"
  color: "fef2c0"
  description: "PR/issue is on hold"

- name: "status: in progress"
  color: "0e8a16"
  description: "Currently being worked on"

- name: "status: blocked"
  color: "d93f0b"
  description: "Blocked by another issue/PR"

# Area Labels (aligned with roadmap)
- name: "area: security"
  color: "ee0701"
  description: "Security enhancements (Phase 1.1)"

- name: "area: ai"
  color: "7057ff"
  description: "AI capabilities and models (Phase 1.2)"

- name: "area: exchange"
  color: "0075ca"
  description: "Exchange integrations (Phase 1.3)"

- name: "area: architecture"
  color: "d4c5f9"
  description: "Project structure refactoring (Phase 1.4)"

- name: "area: ui/ux"
  color: "c2e0c6"
  description: "User experience improvements (Phase 1.5)"

- name: "area: frontend"
  color: "bfdadc"
  description: "Frontend (React/TypeScript)"

- name: "area: backend"
  color: "c5def5"
  description: "Backend (Go)"

- name: "area: api"
  color: "0e8a16"
  description: "API endpoints"

- name: "area: database"
  color: "f9d0c4"
  description: "Database changes"

- name: "area: deployment"
  color: "fbca04"
  description: "Deployment and CI/CD"

# Special Labels
- name: "good first issue"
  color: "7057ff"
  description: "Good for newcomers"

- name: "help wanted"
  color: "008672"
  description: "Extra attention is needed"

- name: "bounty"
  color: "1d76db"
  description: "Bounty available for this issue"

- name: "bounty: claimed"
  color: "5319e7"
  description: "Bounty has been claimed"

- name: "bounty: paid"
  color: "0e8a16"
  description: "Bounty has been paid"

- name: "RFC"
  color: "d4c5f9"
  description: "Request for Comments - needs discussion"

- name: "breaking change"
  color: "d73a4a"
  description: "Includes breaking changes"

- name: "duplicate"
  color: "cfd3d7"
  description: "This issue or pull request already exists"

- name: "invalid"
  color: "e4e669"
  description: "This doesn't seem right"

- name: "wontfix"
  color: "ffffff"
  description: "This will not be worked on"

- name: "dependencies"
  color: "0366d6"
  description: "Dependency updates"

# Roadmap Phases
- name: "roadmap: phase-1"
  color: "0e8a16"
  description: "Core Infrastructure Enhancement"

- name: "roadmap: phase-2"
  color: "fbca04"
  description: "Testing & Stability"

- name: "roadmap: phase-3"
  color: "0075ca"
  description: "Universal Market Expansion"

- name: "roadmap: phase-4"
  color: "7057ff"
  description: "Advanced AI & Automation"

- name: "roadmap: phase-5"
  color: "d73a4a"
  description: "Enterprise & Scaling"


================================================
FILE: .github/workflows/README.md
================================================
# 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)


================================================
FILE: .github/workflows/docker-build.yml
================================================
name: Build and Push Docker Images

on:
  push:
    branches:
      - main
      - dev
      - release/stable
    tags:
      - 'v*'
  pull_request:
    branches:
      - main
      - dev
  workflow_dispatch:

env:
  REGISTRY_GHCR: ghcr.io

jobs:
  prepare:
    name: Prepare repository metadata
    runs-on: ubuntu-22.04
    outputs:
      image_base: ${{ steps.lowercase.outputs.image_base }}
    steps:
      - name: Convert repository name to lowercase
        id: lowercase
        run: |
          REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
          echo "image_base=${REPO_LOWER}" >> $GITHUB_OUTPUT
          echo "Lowercase repository: ${REPO_LOWER}"

  build-and-push:
    name: Build ${{ matrix.name }} (${{ matrix.arch_tag }})
    needs: prepare
    runs-on: ${{ matrix.runner }}
    permissions:
      contents: read
      packages: write
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: backend
            dockerfile: ./docker/Dockerfile.backend
            image_suffix: backend
            platform: linux/amd64
            arch_tag: amd64
            runner: ubuntu-22.04
          - name: backend
            dockerfile: ./docker/Dockerfile.backend
            image_suffix: backend
            platform: linux/arm64
            arch_tag: arm64
            runner: ubuntu-22.04-arm
          - name: frontend
            dockerfile: ./docker/Dockerfile.frontend
            image_suffix: frontend
            platform: linux/amd64
            arch_tag: amd64
            runner: ubuntu-22.04
          - name: frontend
            dockerfile: ./docker/Dockerfile.frontend
            image_suffix: frontend
            platform: linux/arm64
            arch_tag: arm64
            runner: ubuntu-22.04-arm
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY_GHCR }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
        continue-on-error: true

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ${{ env.REGISTRY_GHCR }}/${{ needs.prepare.outputs.image_base }}/nofx-${{ matrix.image_suffix }}
            ${{ secrets.DOCKERHUB_USERNAME && format('{0}/nofx-{1}', secrets.DOCKERHUB_USERNAME, matrix.image_suffix) || '' }}
          tags: |
            type=ref,event=branch,suffix=-${{ matrix.arch_tag }}
            type=semver,pattern={{version}},suffix=-${{ matrix.arch_tag }}
            type=semver,pattern={{major}}.{{minor}},suffix=-${{ matrix.arch_tag }}
            type=semver,pattern={{major}},suffix=-${{ matrix.arch_tag }}
            type=sha,prefix={{branch}}-,suffix=-${{ matrix.arch_tag }}

      - name: Build and push ${{ matrix.name }}-${{ matrix.arch_tag }} image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ${{ matrix.dockerfile }}
          platforms: ${{ matrix.platform }}
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha,scope=${{ matrix.name }}-${{ matrix.arch_tag }}
          cache-to: type=gha,mode=max,scope=${{ matrix.name }}-${{ matrix.arch_tag }}
          build-args: |
            BUILD_DATE=${{ github.event.head_commit.timestamp }}
            VCS_REF=${{ github.sha }}
            VERSION=${{ github.ref_name }}

      - name: Image digest
        run: |
          echo "✅ Built: ${{ matrix.name }}-${{ matrix.arch_tag }}"
          echo "Platform: ${{ matrix.platform }}"
          echo "Tags: ${{ steps.meta.outputs.tags }}"

  create-manifest:
    name: Create multi-arch manifests
    if: github.event_name != 'pull_request'
    needs: [prepare, build-and-push]
    runs-on: ubuntu-22.04
    permissions:
      contents: read
      packages: write
    strategy:
      matrix:
        image_suffix: [backend, frontend]
    steps:
      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY_GHCR }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
        continue-on-error: true

      - name: Create and push multi-arch manifest
        env:
          IMAGE_BASE: ${{ needs.prepare.outputs.image_base }}
        run: |
          # Convert branch name: release/stable -> release-stable (matching Docker metadata action)
          REF_NAME=$(echo "${{ github.ref_name }}" | sed 's/\//-/g')
          GHCR_IMAGE="${{ env.REGISTRY_GHCR }}/${IMAGE_BASE}/nofx-${{ matrix.image_suffix }}"

          echo "📦 Creating manifest for ${{ matrix.image_suffix }}"
          echo "Repository: ${IMAGE_BASE}"
          echo "Image: ${GHCR_IMAGE}"
          echo "Ref name: ${REF_NAME}"

          docker buildx imagetools create -t "${GHCR_IMAGE}:${REF_NAME}" \
            "${GHCR_IMAGE}:${REF_NAME}-amd64" \
            "${GHCR_IMAGE}:${REF_NAME}-arm64"

          # Only main branch gets the 'latest' tag (not dev)
          if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
            docker buildx imagetools create -t "${GHCR_IMAGE}:latest" \
              "${GHCR_IMAGE}:${REF_NAME}-amd64" \
              "${GHCR_IMAGE}:${REF_NAME}-arm64"
            echo "✅ Created latest tag (main branch only)"
          fi

          # release/stable branch gets the 'stable' tag
          if [[ "${{ github.ref }}" == "refs/heads/release/stable" ]]; then
            docker buildx imagetools create -t "${GHCR_IMAGE}:stable" \
              "${GHCR_IMAGE}:${REF_NAME}-amd64" \
              "${GHCR_IMAGE}:${REF_NAME}-arm64"
            echo "✅ Created stable tag (release/stable branch)"
          fi

          if [[ -n "${{ secrets.DOCKERHUB_USERNAME }}" ]]; then
            DOCKERHUB_IMAGE="${{ secrets.DOCKERHUB_USERNAME }}/nofx-${{ matrix.image_suffix }}"
            docker buildx imagetools create -t "${DOCKERHUB_IMAGE}:${REF_NAME}" \
              "${DOCKERHUB_IMAGE}:${REF_NAME}-amd64" \
              "${DOCKERHUB_IMAGE}:${REF_NAME}-arm64" || true
            echo "✅ Created Docker Hub manifest"
          fi

          echo "🎉 Multi-arch manifest created successfully!"


================================================
FILE: .github/workflows/pr-checks-comment.yml
================================================
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: Get PR information
        id: pr-info
        if: steps.backend.outputs.pr_number != '0'
        uses: actions/github-script@v7
        with:
          script: |
            const prNumber = ${{ steps.backend.outputs.pr_number }};

            // Get PR details
            const { data: pr } = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: prNumber
            });

            // Check PR title format (Conventional Commits)
            const prTitle = pr.title;
            const conventionalCommitPattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|security|build)(\(.+\))?: .+/;
            const titleValid = conventionalCommitPattern.test(prTitle);

            core.setOutput('pr_title', prTitle);
            core.setOutput('title_valid', titleValid);

            // Calculate PR size
            const additions = pr.additions;
            const deletions = pr.deletions;
            const total = additions + deletions;

            let size = '';
            let sizeEmoji = '';
            if (total < 300) {
              size = 'Small';
              sizeEmoji = '🟢';
            } else if (total < 1000) {
              size = 'Medium';
              sizeEmoji = '🟡';
            } else {
              size = 'Large';
              sizeEmoji = '🔴';
            }

            core.setOutput('pr_size', size);
            core.setOutput('size_emoji', sizeEmoji);
            core.setOutput('total_lines', total);
            core.setOutput('additions', additions);
            core.setOutput('deletions', deletions);

      - 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';

            // PR Information section
            const prTitle = '${{ steps.pr-info.outputs.pr_title }}';
            const titleValid = '${{ steps.pr-info.outputs.title_valid }}' === 'true';
            const prSize = '${{ steps.pr-info.outputs.pr_size }}';
            const sizeEmoji = '${{ steps.pr-info.outputs.size_emoji }}';
            const totalLines = '${{ steps.pr-info.outputs.total_lines }}';
            const additions = '${{ steps.pr-info.outputs.additions }}';
            const deletions = '${{ steps.pr-info.outputs.deletions }}';

            comment += '### 📋 PR Information\n\n';

            // Title check
            if (titleValid) {
              comment += '**Title Format:** ✅ Good - Follows Conventional Commits\n';
            } else {
              comment += '**Title Format:** ⚠️ Suggestion - Consider using `type(scope): description`\n';
              comment += '<details><summary>Recommended format</summary>\n\n';
              comment += '**Valid types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `security`, `build`\n\n';
              comment += '**Examples:**\n';
              comment += '- `feat(trader): add new trading strategy`\n';
              comment += '- `fix(api): resolve authentication issue`\n';
              comment += '- `docs: update README`\n';
              comment += '</details>\n\n';
            }

            // Size check
            comment += `**PR Size:** ${sizeEmoji} ${prSize} (${totalLines} lines: +${additions} -${deletions})\n`;

            if (prSize === 'Large') {
              comment += '\n💡 **Suggestion:** This is a large PR. Consider breaking it into smaller, focused PRs for easier review.\n';
            }

            comment += '\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/NoFxAiOS/nofx/blob/dev/CONTRIBUTING.md)\n';
            comment += '- [Migration Guide](https://github.com/NoFxAiOS/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/NoFxAiOS/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 pr = pulls.data[0];
            const prNumber = pr.number;

            // Get PR information for fallback comment
            const prTitle = pr.title;
            const conventionalCommitPattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|security|build)(\(.+\))?: .+/;
            const titleValid = conventionalCommitPattern.test(prTitle);

            const additions = pr.additions || 0;
            const deletions = pr.deletions || 0;
            const total = additions + deletions;

            let size = '';
            let sizeEmoji = '';
            if (total < 300) {
              size = 'Small';
              sizeEmoji = '🟢';
            } else if (total < 1000) {
              size = 'Medium';
              sizeEmoji = '🟡';
            } else {
              size = 'Large';
              sizeEmoji = '🔴';
            }

            let comment = '## ⚠️ Advisory Checks - Results Unavailable\n\n';
            comment += 'The advisory checks workflow completed, but results could not be retrieved.\n\n';

            // Add PR Information
            comment += '### 📋 PR Information\n\n';

            if (titleValid) {
              comment += '**Title Format:** ✅ Good - Follows Conventional Commits\n';
            } else {
              comment += '**Title Format:** ⚠️ Suggestion - Consider using `type(scope): description`\n';
            }

            comment += `**PR Size:** ${sizeEmoji} ${size} (${total} lines: +${additions} -${deletions})\n\n`;

            if (size === 'Large') {
              comment += '💡 **Suggestion:** This is a large PR. Consider breaking it into smaller, focused PRs for easier review.\n\n';
            }

            comment += '---\n\n';
            comment += '### ⚠️ Backend/Frontend Check Results\n\n';
            comment += 'Results could not be retrieved.\n\n';
            comment += '**Possible reasons:**\n';
            comment += '- Artifacts were not uploaded successfully\n';
            comment += '- Artifacts expired (retention: 1 day)\n';
            comment += '- Permission issues\n\n';
            comment += '**What to do:**\n';
            comment += `1. Check the [PR Checks - Run workflow](${context.payload.workflow_run?.html_url || 'logs'}) logs\n`;
            comment += '2. Ensure your code passes local checks:\n';
            comment += '```bash\n';
            comment += '# Backend\n';
            comment += 'go fmt ./...\n';
            comment += 'go vet ./...\n';
            comment += 'go build\n';
            comment += 'go test ./...\n\n';
            comment += '# Frontend (if applicable)\n';
            comment += 'cd web\n';
            comment += 'npm run build\n';
            comment += '```\n\n';
            comment += '---\n\n';
            comment += '*This is an automated fallback message. The advisory checks ran but results are not available.*';

            await github.rest.issues.createComment({
              issue_number: prNumber,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: comment
            });


================================================
FILE: .github/workflows/pr-checks-run.yml
================================================
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


================================================
FILE: .github/workflows/pr-checks.yml
================================================
name: PR Checks

on:
  pull_request:
    types: [opened, synchronize, reopened, edited]
    branches:
      - 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 }}
        with:
          types: |
            feat
            fix
            docs
            style
            refactor
            perf
            test
            chore
            ci
            security
            build
          scopes: |
            exchange
            trader
            ai
            api
            ui
            frontend
            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;
            const additions = pr.additions;
            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 = '';

            if (total < 300) {
              label = 'size: small';
              comment = '✅ This PR is **small** and easy to review!';
            } else if (total < 1000) {
              label = 'size: medium';
              comment = '⚠️ This PR is **medium** sized. Consider breaking it into smaller PRs if possible.';
            } else {
              label = 'size: large';
              comment = '🚨 This PR is **large** (>' + total + ' lines changed). Please consider breaking it into smaller, focused PRs for easier review.';
            }

            // 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
                  });
                }
              } 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 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

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'

      - name: Cache Go modules
        uses: actions/cache@v4
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Download dependencies
        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 "⚠️ 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: Build
        run: go build -v -o nofx

  # 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

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Cache Node modules
        uses: actions/cache@v4
        with:
          path: web/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('web/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies
        working-directory: ./web
        run: npm ci

      - 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:
          configuration-path: .github/labeler.yml
          repo-token: ${{ secrets.GITHUB_TOKEN }}

  # Check for security issues
  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

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: Upload Trivy results
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'

  # Check for secrets in code
  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 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-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: |
          # 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 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


================================================
FILE: .github/workflows/pr-docker-check.yml
================================================
name: PR Docker Build Check

# Lightweight build check on PR only, no image push
# Strategy: Quick verify amd64 + spot check arm64 (backend only)
on:
  pull_request:
    branches:
      - main
      - dev
    paths:
      - 'docker/**'
      - 'Dockerfile*'
      - 'go.mod'
      - 'go.sum'
      - '**.go'
      - 'web/**'
      - '.github/workflows/docker-build.yml'
      - '.github/workflows/pr-docker-check.yml'

jobs:
  # Quick check: amd64 builds for all images
  docker-build-amd64:
    name: Build Check (amd64)
    runs-on: ubuntu-22.04
    permissions:
      contents: read

    strategy:
      fail-fast: false
      matrix:
        include:
          - name: backend
            dockerfile: ./docker/Dockerfile.backend
            test_run: true  # Needs test run
          - name: frontend
            dockerfile: ./docker/Dockerfile.frontend
            test_run: true

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build ${{ matrix.name }} image (amd64)
        id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ${{ matrix.dockerfile }}
          platforms: linux/amd64
          push: false
          load: true  # Load into local Docker for test run
          tags: nofx-${{ matrix.name }}:pr-test
          cache-from: type=gha,scope=${{ matrix.name }}-amd64
          cache-to: type=gha,mode=max,scope=${{ matrix.name }}-amd64
          build-args: |
            BUILD_DATE=${{ github.event.pull_request.updated_at }}
            VCS_REF=${{ github.event.pull_request.head.sha }}
            VERSION=pr-${{ github.event.pull_request.number }}

      - name: Test run container (smoke test)
        if: matrix.test_run
        timeout-minutes: 2
        run: |
          echo "🧪 Testing container startup..."

          # Start container
          docker run -d --name test-${{ matrix.name }} \
            --health-cmd="exit 0" \
            nofx-${{ matrix.name }}:pr-test

          # Wait for container to start (up to 30 seconds)
          for i in {1..30}; do
            if docker ps | grep -q test-${{ matrix.name }}; then
              echo "✅ Container started successfully"
              docker logs test-${{ matrix.name }}
              docker stop test-${{ matrix.name }} || true
              exit 0
            fi
            sleep 1
          done

          echo "❌ Container failed to start"
          docker logs test-${{ matrix.name }} || true
          exit 1

      - name: Check image size
        run: |
          SIZE=$(docker image inspect nofx-${{ matrix.name }}:pr-test --format='{{.Size}}')
          SIZE_MB=$((SIZE / 1024 / 1024))

          echo "📦 Image size: ${SIZE_MB} MB"

          # Warning thresholds
          if [ "${{ matrix.name }}" = "backend" ] && [ $SIZE_MB -gt 500 ]; then
            echo "⚠️  Warning: Backend image is larger than 500MB"
          elif [ "${{ matrix.name }}" = "frontend" ] && [ $SIZE_MB -gt 200 ]; then
            echo "⚠️  Warning: Frontend image is larger than 200MB"
          else
            echo "✅ Image size is reasonable"
          fi

  # ARM64 native build check: Uses GitHub native ARM64 runner (fast!)
  docker-build-arm64-native:
    name: Build Check (arm64 native - backend)
    runs-on: ubuntu-22.04-arm  # Native ARM64 runner
    permissions:
      contents: read

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      # Native ARM64 does not need QEMU, builds directly
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build backend image (arm64 native)
        uses: docker/build-push-action@v5
        timeout-minutes: 15  # Native builds are faster!
        with:
          context: .
          file: ./docker/Dockerfile.backend
          platforms: linux/arm64
          push: false
          load: true  # Load locally for testing
          tags: nofx-backend:pr-test-arm64
          cache-from: type=gha,scope=backend-arm64
          cache-to: type=gha,mode=max,scope=backend-arm64
          build-args: |
            BUILD_DATE=${{ github.event.pull_request.updated_at }}
            VCS_REF=${{ github.event.pull_request.head.sha }}
            VERSION=pr-${{ github.event.pull_request.number }}

      - name: Test run ARM64 container
        timeout-minutes: 2
        run: |
          echo "🧪 Testing ARM64 container startup..."

          # Start container
          docker run -d --name test-backend-arm64 \
            --health-cmd="exit 0" \
            nofx-backend:pr-test-arm64

          # Wait for startup
          for i in {1..30}; do
            if docker ps | grep -q test-backend-arm64; then
              echo "✅ ARM64 container started successfully"
              docker logs test-backend-arm64
              docker stop test-backend-arm64 || true
              exit 0
            fi
            sleep 1
          done

          echo "❌ ARM64 container failed to start"
          docker logs test-backend-arm64 || true
          exit 1

      - name: ARM64 build summary
        run: |
          echo "✅ Backend ARM64 native build successful!"
          echo "Using GitHub native ARM64 runner - no QEMU needed!"
          echo "Build time is ~3x faster than emulation"

  # Aggregate check results
  check-summary:
    name: Docker Build Summary
    needs: [docker-build-amd64, docker-build-arm64-native]
    runs-on: ubuntu-22.04
    if: always()
    permissions:
      pull-requests: write  # For posting comments
    steps:
      - name: Check build results
        id: check
        run: |
          echo "## 🐳 Docker Build Check Results" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY

          # Check amd64 build
          if [[ "${{ needs.docker-build-amd64.result }}" == "success" ]]; then
            echo "✅ **AMD64 builds**: All passed" >> $GITHUB_STEP_SUMMARY
            AMD64_OK=true
          else
            echo "❌ **AMD64 builds**: Failed" >> $GITHUB_STEP_SUMMARY
            AMD64_OK=false
          fi

          # Check arm64 build
          if [[ "${{ needs.docker-build-arm64-native.result }}" == "success" ]]; then
            echo "✅ **ARM64 build** (native): Backend passed (frontend will be verified after merge)" >> $GITHUB_STEP_SUMMARY
            ARM64_OK=true
          else
            echo "❌ **ARM64 build** (native): Backend failed" >> $GITHUB_STEP_SUMMARY
            ARM64_OK=false
          fi

          echo "" >> $GITHUB_STEP_SUMMARY

          if [ "$AMD64_OK" = true ] && [ "$ARM64_OK" = true ]; then
            echo "### 🎉 All checks passed!" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
            echo "After merge:" >> $GITHUB_STEP_SUMMARY
            echo "- Full multi-arch builds (amd64 + arm64) will run in parallel" >> $GITHUB_STEP_SUMMARY
            echo "- Estimated time: 15-20 minutes" >> $GITHUB_STEP_SUMMARY
            exit 0
          else
            echo "### ❌ Build checks failed" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
            echo "Please check the build logs above and fix the errors." >> $GITHUB_STEP_SUMMARY
            exit 1
          fi

      - name: Comment on PR
        if: always() && github.event.pull_request.head.repo.full_name == github.repository
        uses: actions/github-script@v7
        with:
          script: |
            const amd64Status = '${{ needs.docker-build-amd64.result }}';
            const arm64Status = '${{ needs.docker-build-arm64-native.result }}';

            const successIcon = '✅';
            const failIcon = '❌';

            const comment = [
              '## 🐳 Docker Build Check Results',
              '',
              `**AMD64 builds**: ${amd64Status === 'success' ? successIcon : failIcon} ${amd64Status}`,
              `**ARM64 build** (native runner): ${arm64Status === 'success' ? successIcon : failIcon} ${arm64Status}`,
              '',
              amd64Status === 'success' && arm64Status === 'success'
                ? '### 🎉 All Docker builds passed!\n\n✨ Using GitHub native ARM64 runners - 3x faster than emulation!\n\nAfter merge, full multi-arch builds will run in ~10-12 minutes.'
                : '### ⚠️ Some builds failed\n\nPlease check the Actions tab for details.',
              '',
              '<sub>Checked: Backend (amd64 + arm64 native), Frontend (amd64) | Powered by GitHub ARM64 Runners</sub>'
            ].join('\n');

            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.pull_request.number,
              body: comment
            });


================================================
FILE: .github/workflows/pr-docker-compose-healthcheck.yml
================================================
name: PR Docker Compose Healthcheck

# Verify docker-compose.yml healthcheck config works correctly in Alpine containers
on:
  pull_request:
    branches:
      - main
      - dev
    paths:
      - 'docker-compose.yml'
      - 'docker/Dockerfile.backend'
      - 'docker/Dockerfile.frontend'
      - '.github/workflows/pr-docker-compose-healthcheck.yml'

jobs:
  healthcheck-test:
    name: Test Docker Compose Healthcheck
    runs-on: ubuntu-22.04
    timeout-minutes: 10
    permissions:
      contents: read

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Create minimal .env for testing
        run: |
          cat > .env <<EOF
          # Minimal config for healthcheck testing
          NOFX_BACKEND_PORT=8080
          NOFX_FRONTEND_PORT=3000
          NOFX_TIMEZONE=UTC
          DATA_ENCRYPTION_KEY=test-key-32-chars-minimum-length
          JWT_SECRET=test-jwt-secret-minimum-32-chars
          EOF

      - name: Create minimal config.json
        run: |
          cat > config.json <<EOF
          {
            "ai_models": [],
            "exchanges": [],
            "traders": []
          }
          EOF

      - name: Start services with docker compose
        run: |
          docker compose up -d
          echo "✅ Services started, waiting for healthcheck..."

      - name: Wait for healthcheck start_period
        run: |
          echo "⏳ Waiting 70 seconds for healthcheck start_period to complete..."
          sleep 70

      - name: Verify backend healthcheck
        run: |
          echo "🔍 Checking backend container health..."

          BACKEND_HEALTH=$(docker inspect nofx-trading --format='{{.State.Health.Status}}')
          echo "Backend health status: $BACKEND_HEALTH"

          if [ "$BACKEND_HEALTH" != "healthy" ]; then
            echo "❌ Backend container is not healthy!"
            echo "Health status: $BACKEND_HEALTH"
            echo ""
            echo "Health logs:"
            docker inspect nofx-trading --format='{{json .State.Health}}' | jq
            echo ""
            echo "Container logs:"
            docker logs nofx-trading
            exit 1
          fi

          echo "✅ Backend container is healthy"

      - name: Verify frontend healthcheck
        run: |
          echo "🔍 Checking frontend container health..."

          FRONTEND_HEALTH=$(docker inspect nofx-frontend --format='{{.State.Health.Status}}')
          echo "Frontend health status: $FRONTEND_HEALTH"

          if [ "$FRONTEND_HEALTH" != "healthy" ]; then
            echo "❌ Frontend container is not healthy!"
            echo "Health status: $FRONTEND_HEALTH"
            echo ""
            echo "Health logs:"
            docker inspect nofx-frontend --format='{{json .State.Health}}' | jq
            echo ""
            echo "Container logs:"
            docker logs nofx-frontend
            exit 1
          fi

          echo "✅ Frontend container is healthy"

      - name: Verify healthcheck commands are Alpine-compatible
        run: |
          echo "🔍 Verifying healthcheck commands use Alpine-compatible tools..."

          # Check that docker-compose.yml uses wget (not curl)
          if grep -q 'test:.*curl' docker-compose.yml; then
            echo "❌ ERROR: docker-compose.yml uses 'curl' which doesn't exist in Alpine!"
            echo ""
            echo "Alpine Linux (used by our containers) includes 'wget' but not 'curl'."
            echo "Please use 'wget --no-verbose --tries=1 --spider' instead."
            exit 1
          fi

          if ! grep -q 'test:.*wget' docker-compose.yml; then
            echo "⚠️  WARNING: No wget healthcheck found in docker-compose.yml"
          else
            echo "✅ Healthcheck uses Alpine-compatible 'wget' command"
          fi

      - name: Test healthcheck commands inside containers
        run: |
          echo "🧪 Testing healthcheck commands directly..."

          # Test backend healthcheck command
          echo "Testing backend healthcheck..."
          docker exec nofx-trading wget --no-verbose --tries=1 --spider http://localhost:8080/api/health
          echo "✅ Backend healthcheck command works"

          # Test frontend healthcheck command
          echo "Testing frontend healthcheck..."
          docker exec nofx-frontend wget --no-verbose --tries=1 --spider http://127.0.0.1/health
          echo "✅ Frontend healthcheck command works"

      - name: Show container status
        if: always()
        run: |
          echo "📊 Final container status:"
          docker ps --format "table {{.Names}}\t{{.Status}}"

      - name: Show logs on failure
        if: failure()
        run: |
          echo "📋 Backend logs:"
          docker logs nofx-trading
          echo ""
          echo "📋 Frontend logs:"
          docker logs nofx-frontend

      - name: Cleanup
        if: always()
        run: |
          docker compose down -v
          rm -f .env config.json


================================================
FILE: .github/workflows/pr-go-test-coverage.yml
================================================
name: Go Test Coverage

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - dev
      - main
  push:
    branches:
      - dev
      - main

permissions:
  contents: read
  pull-requests: write

jobs:
  test-coverage:
    name: Go Unit Tests & Coverage
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.25'

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install Python dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r .github/workflows/scripts/requirements.txt

      - name: Cache Go modules
        uses: actions/cache@v4
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Download dependencies
        run: go mod download

      - name: Verify Go coverage tool
        run: |
          go tool cover -h || echo "Warning: go tool cover not available"

      - name: Run tests with coverage
        env:
          DATA_ENCRYPTION_KEY: "test-encryption-key-for-ci-only-not-production"
        run: |
          go test -v -race -coverprofile=coverage.out -covermode=atomic ./...

      - name: Calculate coverage and generate report
        id: coverage
        run: |
          chmod +x .github/workflows/scripts/calculate_coverage.py
          python .github/workflows/scripts/calculate_coverage.py coverage.out coverage_report.md

      - name: Comment PR with coverage
        if: github.event_name == 'pull_request'
        continue-on-error: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          chmod +x .github/workflows/scripts/comment_pr.py
          python .github/workflows/scripts/comment_pr.py \
            ${{ github.event.pull_request.number }} \
            "${{ steps.coverage.outputs.coverage }}" \
            "${{ steps.coverage.outputs.emoji }}" \
            "${{ steps.coverage.outputs.status }}" \
            "${{ steps.coverage.outputs.badge_color }}" \
            coverage_report.md


================================================
FILE: .github/workflows/pr-template-suggester.yml
================================================
name: PR Labeler

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  pull-requests: write
  contents: read

jobs:
  label-pr:
    runs-on: ubuntu-latest
    steps:
      - name: Analyze PR and apply labels
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const { data: files } = await github.rest.pulls.listFiles({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number,
              per_page: 100,
            });

            let goFiles = 0, jsFiles = 0, tsFiles = 0, mdFiles = 0, otherFiles = 0;
            let additions = 0, deletions = 0;

            for (const file of files) {
              const name = file.filename.toLowerCase();
              additions += file.additions || 0;
              deletions += file.deletions || 0;
              if (name.endsWith('.go')) goFiles++;
              else if (name.endsWith('.js') || name.endsWith('.jsx')) jsFiles++;
              else if (name.endsWith('.ts') || name.endsWith('.tsx') || name.endsWith('.vue')) tsFiles++;
              else if (name.endsWith('.md')) mdFiles++;
              else otherFiles++;
            }

            const totalFiles = goFiles + jsFiles + tsFiles + mdFiles + otherFiles;
            if (totalFiles === 0) return;

            // --- Scope label ---
            const labels = [];
            if (goFiles / totalFiles > 0.5) labels.push('backend');
            else if ((jsFiles + tsFiles) / totalFiles > 0.5) labels.push('frontend');
            else if (mdFiles / totalFiles > 0.7) labels.push('documentation');
            else labels.push('fullstack');

            // --- Size label (like OpenClaw) ---
            const totalChanged = additions + deletions;
            const sizeLabels = ['size: XS', 'size: S', 'size: M', 'size: L', 'size: XL'];
            let sizeLabel = 'size: XL';
            if (totalChanged < 50) sizeLabel = 'size: XS';
            else if (totalChanged < 200) sizeLabel = 'size: S';
            else if (totalChanged < 500) sizeLabel = 'size: M';
            else if (totalChanged < 1000) sizeLabel = 'size: L';
            labels.push(sizeLabel);

            // Ensure size labels exist
            for (const sl of sizeLabels) {
              try {
                await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: sl });
              } catch (e) {
                if (e.status === 404) {
                  await github.rest.issues.createLabel({ owner: context.repo.owner, repo: context.repo.repo, name: sl, color: 'b76e79' });
                }
              }
            }

            // Remove stale size labels
            const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
              owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number,
            });
            for (const cl of currentLabels) {
              if (sizeLabels.includes(cl.name) && cl.name !== sizeLabel) {
                await github.rest.issues.removeLabel({
                  owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, name: cl.name,
                }).catch(() => {});
              }
            }

            // Apply labels
            await github.rest.issues.addLabels({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              labels: labels,
            });

            console.log(`Applied labels: ${labels.join(', ')} (${totalChanged} lines changed)`);


================================================
FILE: .github/workflows/scripts/calculate_coverage.py
================================================
#!/usr/bin/env python3
"""
Calculate Go test coverage and generate reports.

This script parses the coverage.out file generated by `go test -coverprofile`,
extracts coverage statistics, and generates formatted reports.
"""

import sys
import re
import os
from typing import Dict, List, Tuple


def parse_coverage_file(coverage_file: str) -> Tuple[float, Dict[str, float]]:
    """
    Parse coverage output file and extract coverage data.

    Args:
        coverage_file: Path to coverage.out file

    Returns:
        Tuple of (total_coverage, package_coverage_dict)
    """
    if not os.path.exists(coverage_file):
        print(f"Error: Coverage file {coverage_file} not found", file=sys.stderr)
        sys.exit(1)

    # Run go tool cover to get coverage data
    import subprocess

    try:
        result = subprocess.run(
            ['go', 'tool', 'cover', '-func', coverage_file],
            capture_output=True,
            text=True,
            check=True
        )
    except subprocess.CalledProcessError as e:
        print(f"Error running go tool cover: {e}", file=sys.stderr)
        sys.exit(1)

    lines = result.stdout.strip().split('\n')
    package_coverage = {}
    total_coverage = 0.0

    for line in lines:
        # Skip empty lines
        if not line.strip():
            continue

        # Check for total coverage line
        if line.startswith('total:'):
            # Extract percentage from "total: (statements) XX.X%"
            match = re.search(r'(\d+\.\d+)%', line)
            if match:
                total_coverage = float(match.group(1))
            continue

        # Parse package/file coverage
        # Format: "package/file.go:function statements coverage%"
        parts = line.split()
        if len(parts) >= 3:
            file_path = parts[0]
            coverage_str = parts[-1]

            # Extract package name from file path
            package = file_path.split(':')[0]
            package_name = '/'.join(package.split('/')[:-1]) if '/' in package else package

            # Extract coverage percentage
            match = re.search(r'(\d+\.\d+)%', coverage_str)
            if match:
                coverage_pct = float(match.group(1))

                # Aggregate by package
                if package_name not in package_coverage:
                    package_coverage[package_name] = []
                package_coverage[package_name].append(coverage_pct)

    # Calculate average coverage per package
    package_avg = {
        pkg: sum(coverages) / len(coverages)
        for pkg, coverages in package_coverage.items()
    }

    return total_coverage, package_avg


def get_coverage_status(coverage: float) -> Tuple[str, str, str]:
    """
    Get coverage status based on percentage.

    Args:
        coverage: Coverage percentage

    Returns:
        Tuple of (emoji, status_text, badge_color)
    """
    if coverage >= 80:
        return '🟢', 'excellent', 'brightgreen'
    elif coverage >= 60:
        return '🟡', 'good', 'yellow'
    elif coverage >= 40:
        return '🟠', 'fair', 'orange'
    else:
        return '🔴', 'needs improvement', 'red'


def generate_coverage_report(coverage_file: str, output_file: str) -> None:
    """
    Generate a detailed coverage report in markdown format.

    Args:
        coverage_file: Path to coverage.out file
        output_file: Path to output markdown file
    """
    import subprocess

    try:
        result = subprocess.run(
            ['go', 'tool', 'cover', '-func', coverage_file],
            capture_output=True,
            text=True,
            check=True
        )
    except subprocess.CalledProcessError as e:
        print(f"Error generating coverage report: {e}", file=sys.stderr)
        sys.exit(1)

    with open(output_file, 'w') as f:
        f.write("## Coverage by Package\n\n")
        f.write("```\n")
        f.write(result.stdout)
        f.write("```\n")


def set_github_output(name: str, value: str) -> None:
    """
    Set GitHub Actions output variable.

    Args:
        name: Output variable name
        value: Output variable value
    """
    github_output = os.environ.get('GITHUB_OUTPUT')
    if github_output:
        with open(github_output, 'a') as f:
            f.write(f"{name}={value}\n")
    else:
        print(f"::set-output name={name}::{value}")


def main():
    """Main entry point."""
    if len(sys.argv) < 2:
        print("Usage: calculate_coverage.py <coverage_file> [output_file]", file=sys.stderr)
        sys.exit(1)

    coverage_file = sys.argv[1]
    output_file = sys.argv[2] if len(sys.argv) > 2 else 'coverage_report.md'

    # Parse coverage data
    total_coverage, package_coverage = parse_coverage_file(coverage_file)

    # Get coverage status
    emoji, status, badge_color = get_coverage_status(total_coverage)

    # Generate detailed report
    generate_coverage_report(coverage_file, output_file)

    # Output results
    print(f"Total Coverage: {total_coverage}%")
    print(f"Status: {status}")
    print(f"Badge Color: {badge_color}")

    # Set GitHub Actions outputs
    set_github_output('coverage', f'{total_coverage}%')
    set_github_output('coverage_num', str(total_coverage))
    set_github_output('status', status)
    set_github_output('emoji', emoji)
    set_github_output('badge_color', badge_color)

    # Print package breakdown
    if package_coverage:
        print("\nCoverage by Package:")
        for package, coverage in sorted(package_coverage.items()):
            print(f"  {package}: {coverage:.1f}%")


if __name__ == '__main__':
    main()


================================================
FILE: .github/workflows/scripts/comment_pr.py
================================================
#!/usr/bin/env python3
"""
Post or update coverage report comment on GitHub Pull Request.

This script generates a formatted coverage report comment and posts it to a PR,
or updates an existing coverage comment if one already exists.
"""

import os
import sys
import json
import requests
from typing import Optional


def read_file(file_path: str) -> str:
    """Read file content."""
    try:
        with open(file_path, 'r') as f:
            return f.read()
    except FileNotFoundError:
        print(f"Warning: File {file_path} not found", file=sys.stderr)
        return ""


def generate_comment_body(coverage: str, emoji: str, status: str,
                          badge_color: str, coverage_report_path: str) -> str:
    """
    Generate the PR comment body.

    Args:
        coverage: Coverage percentage (e.g., "75.5%")
        emoji: Status emoji
        status: Status text
        badge_color: Badge color
        coverage_report_path: Path to detailed coverage report

    Returns:
        Formatted comment body in markdown
    """
    coverage_report = read_file(coverage_report_path)

    # URL encode the coverage percentage for the badge
    coverage_encoded = coverage.replace('%', '%25')

    comment = f"""## {emoji} Go Test Coverage Report

**Total Coverage:** `{coverage}` ({status})

![Coverage](https://img.shields.io/badge/coverage-{coverage_encoded}-{badge_color})

<details>
<summary>📊 Detailed Coverage Report (click to expand)</summary>

{coverage_report}

</details>

### Coverage Guidelines
- 🟢 >= 80%: Excellent
- 🟡 >= 60%: Good
- 🟠 >= 40%: Fair
- 🔴 < 40%: Needs improvement

---
*This is an automated coverage report. The coverage requirement is advisory and does not block PR merging.*
"""
    return comment


def find_existing_comment(token: str, repo: str, pr_number: int) -> Optional[int]:
    """
    Find existing coverage comment in the PR.

    Args:
        token: GitHub token
        repo: Repository in format "owner/repo"
        pr_number: Pull request number

    Returns:
        Comment ID if found, None otherwise
    """
    url = f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments"
    headers = {
        'Authorization': f'token {token}',
        'Accept': 'application/vnd.github.v3+json'
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        comments = response.json()

        # Look for existing coverage comment
        for comment in comments:
            if (comment.get('user', {}).get('type') == 'Bot' and
                'Go Test Coverage Report' in comment.get('body', '')):
                return comment['id']

    except requests.exceptions.RequestException as e:
        print(f"Error fetching comments: {e}", file=sys.stderr)

    return None


def post_comment(token: str, repo: str, pr_number: int, body: str) -> bool:
    """
    Post a new comment to the PR.

    Args:
        token: GitHub token
        repo: Repository in format "owner/repo"
        pr_number: Pull request number
        body: Comment body

    Returns:
        True if successful, False otherwise
    """
    url = f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments"
    headers = {
        'Authorization': f'token {token}',
        'Accept': 'application/vnd.github.v3+json'
    }
    data = {'body': body}

    try:
        response = requests.post(url, headers=headers, json=data)
        response.raise_for_status()
        print("✅ Coverage comment posted successfully")
        return True
    except requests.exceptions.RequestException as e:
        print(f"Error posting comment: {e}", file=sys.stderr)
        if hasattr(e, 'response') and e.response is not None:
            print(f"Response: {e.response.text}", file=sys.stderr)
        return False


def update_comment(token: str, repo: str, comment_id: int, body: str) -> bool:
    """
    Update an existing comment.

    Args:
        token: GitHub token
        repo: Repository in format "owner/repo"
        comment_id: Comment ID to update
        body: New comment body

    Returns:
        True if successful, False otherwise
    """
    url = f"https://api.github.com/repos/{repo}/issues/comments/{comment_id}"
    headers = {
        'Authorization': f'token {token}',
        'Accept': 'application/vnd.github.v3+json'
    }
    data = {'body': body}

    try:
        response = requests.patch(url, headers=headers, json=data)
        response.raise_for_status()
        print("✅ Coverage comment updated successfully")
        return True
    except requests.exceptions.RequestException as e:
        print(f"Error updating comment: {e}", file=sys.stderr)
        if hasattr(e, 'response') and e.response is not None:
            print(f"Response: {e.response.text}", file=sys.stderr)
        return False


def is_fork_pr(event_path: str) -> bool:
    """
    Check if the PR is from a fork.

    Args:
        event_path: Path to GitHub event JSON file

    Returns:
        True if fork PR, False otherwise
    """
    try:
        with open(event_path, 'r') as f:
            event = json.load(f)

        pr = event.get('pull_request', {})
        head_repo = pr.get('head', {}).get('repo', {}).get('full_name')
        base_repo = pr.get('base', {}).get('repo', {}).get('full_name')

        return head_repo != base_repo
    except (FileNotFoundError, json.JSONDecodeError, KeyError) as e:
        print(f"Warning: Could not determine if fork PR: {e}", file=sys.stderr)
        return False


def main():
    """Main entry point."""
    # Get environment variables
    token = os.environ.get('GITHUB_TOKEN')
    repo = os.environ.get('GITHUB_REPOSITORY')
    event_path = os.environ.get('GITHUB_EVENT_PATH', '')

    # Get arguments
    if len(sys.argv) < 6:
        print("Usage: comment_pr.py <pr_number> <coverage> <emoji> <status> <badge_color> [coverage_report_path]",
              file=sys.stderr)
        sys.exit(1)

    pr_number = int(sys.argv[1])
    coverage = sys.argv[2]
    emoji = sys.argv[3]
    status = sys.argv[4]
    badge_color = sys.argv[5]
    coverage_report_path = sys.argv[6] if len(sys.argv) > 6 else 'coverage_report.md'

    # Validate environment
    if not token:
        print("Error: GITHUB_TOKEN environment variable not set", file=sys.stderr)
        sys.exit(1)

    if not repo:
        print("Error: GITHUB_REPOSITORY environment variable not set", file=sys.stderr)
        sys.exit(1)

    # Check if fork PR
    if event_path and is_fork_pr(event_path):
        print("ℹ️  Fork PR detected - skipping comment (no write permissions)")
        sys.exit(0)

    # Generate comment body
    comment_body = generate_comment_body(coverage, emoji, status, badge_color, coverage_report_path)

    # Check for existing comment
    existing_comment_id = find_existing_comment(token, repo, pr_number)

    # Post or update comment
    if existing_comment_id:
        print(f"Found existing comment (ID: {existing_comment_id}), updating...")
        success = update_comment(token, repo, existing_comment_id, comment_body)
    else:
        print("No existing comment found, creating new one...")
        success = post_comment(token, repo, pr_number, comment_body)

    sys.exit(0 if success else 1)


if __name__ == '__main__':
    main()


================================================
FILE: .github/workflows/scripts/requirements.txt
================================================
# Python dependencies for GitHub Actions scripts
requests>=2.31.0


================================================
FILE: .github/workflows/test.yml
================================================
name: Test

on:
  push:
    branches: [main, dev]
  pull_request:
    branches: [main, dev]

jobs:
  backend-tests:
    name: Backend Tests
    runs-on: ubuntu-latest
    continue-on-error: true  # Don't block PRs

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.23'

      - name: Download dependencies
        run: go mod download

      - name: Run tests
        run: go test -v ./...

      - name: Generate coverage
        run: go test -coverprofile=coverage.out ./...
        continue-on-error: true

  frontend-tests:
    name: Frontend Tests
    runs-on: ubuntu-latest
    continue-on-error: true  # Don't block PRs

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: web/package-lock.json

      - name: Install dependencies
        run: cd web && npm ci

      - name: Run tests
        run: cd web && npm run test


================================================
FILE: .gitignore
================================================
# IDE 配置文件
.idea/
*.iml
*.xml

# AI 工具
.claude/
CLAUDE.md

# 编译产物
nofx-auto
*.exe
nofx
nofx_test

# Go 相关
*.test
*.out
.gocache/

# 操作系统
.DS_Store
Thumbs.db

# 临时文件
*.log
*.tmp
*.bak
*.backup

# 环境变量
.env
config.json
configbak.json

# 数据目录(数据库、日志等)
data/
*.db

# 决策日志
decision_logs/
nofx_test

# Node.js
web/node_modules/
node_modules/
web/dist/
web/.vite/

# ESLint 临时报告文件(调试时生成,不纳入版本控制)
eslint-*.json

# VS code
.vscode

# 密钥和敏感文件
# 注意:crypto目录包含加密服务代码,应该被提交
# 只忽略密钥文件本身
secrets/
*.key
*.pem
*.p12
*.pfx
rsa_key*

# 加密相关
DATA_ENCRYPTION_KEY=*
*.enc

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# Python 虚拟环境
.venv/
venv/
ENV/
env/
.env/

# uv
.uv/
uv.lock

# Pytest
.pytest_cache/
.coverage
htmlcov/
*.cover
.hypothesis/

# Jupyter Notebook
.ipynb_checkpoints
*.ipynb

# pyenv
.python-version

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/
PR_DESCRIPTION.md


================================================
FILE: .husky/_/husky.sh
================================================
#!/usr/bin/env sh
if [ -z "$husky_skip_init" ]; then
  debug () {
    if [ "$HUSKY_DEBUG" = "1" ]; then
      echo "husky (debug) - $1"
    fi
  }

  readonly hook_name="$(basename -- "$0")"
  debug "starting $hook_name..."

  if [ "$HUSKY" = "0" ]; then
    debug "HUSKY env variable is set to 0, skipping hook"
    exit 0
  fi

  if [ -f ~/.huskyrc ]; then
    debug "sourcing ~/.huskyrc"
    . ~/.huskyrc
  fi

  readonly husky_skip_init=1
  export husky_skip_init
  sh -e "$0" "$@"
  exitCode="$?"

  if [ $exitCode != 0 ]; then
    echo "husky - $hook_name hook exited with code $exitCode (error)"
  fi

  if [ $exitCode = 127 ]; then
    echo "husky - command not found in PATH=$PATH"
  fi

  exit $exitCode
fi


================================================
FILE: .husky/pre-commit
================================================
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

cd web && npx lint-staged


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to the NOFX project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

**Languages:** [English](CHANGELOG.md) | [中文](CHANGELOG.zh-CN.md)

---

## [Unreleased]

### Added
- Documentation system with multi-language support (EN/CN/RU/UK)
- Complete getting-started guides (Docker, Custom API)
- Architecture documentation with system design details
- User guides with FAQ and troubleshooting
- Community documentation with bounty programs

### Changed
- Reorganized documentation structure into logical categories
- Updated all README files with proper navigation links

---

## [3.0.0] - 2025-10-30

### Added - Major Architecture Transformation 🚀

**Complete System Redesign - Web-Based Configuration Platform**

This is a **major breaking update** that completely transforms NOFX from a static config-based system to a modern web-based trading platform.

#### Database-Driven Architecture
- SQLite integration replacing static JSON config
- Persistent storage with automatic timestamps
- Foreign key relationships and triggers for data consistency
- Separate tables for AI models, exchanges, traders, and system config

#### Web-Based Configuration Interface
- Complete web-based configuration management (no more JSON editing)
- AI Model setup through web interface (DeepSeek/Qwen API keys)
- Exchange management (Binance/Hyperliquid credentials)
- Dynamic trader creation (combine any AI model with any exchange)
- Real-time control (start/stop traders without system restart)

#### Flexible Architecture
- Separation of concerns (AI models and exchanges independent)
- Mix & match capability (unlimited combinations)
- Scalable design (support for unlimited traders)
- Clean slate approach (no default traders)

#### Enhanced API Layer
- RESTful design with complete CRUD operations
- New endpoints:
  - `GET/PUT /api/models` - AI model configuration
  - `GET/PUT /api/exchanges` - Exchange configuration
  - `POST/DELETE /api/traders` - Trader management
  - `POST /api/traders/:id/start|stop` - Trader control
- Updated documentation for all API endpoints

#### Modernized Codebase
- Type safety with proper separation of configuration types
- Database abstraction with prepared statements
- Comprehensive error handling and validation
- Better code organization (database, API, business logic)

### Changed
- **BREAKING**: Old `config.json` files no longer used
- Configuration must be done through web interface
- Much easier setup and better UX
- No more server restarts for configuration changes

### Why This Matters
- 🎯 **User Experience**: Much easier to configure and manage
- 🔧 **Flexibility**: Create any combination of AI models and exchanges
- 📊 **Scalability**: Support for complex multi-trader setups
- 🔒 **Reliability**: Database ensures data persistence and consistency
- 🚀 **Future-Proof**: Foundation for advanced features

---

## [2.0.2] - 2025-10-29

### Fixed - Critical Bug Fixes: Trade History & Performance Analysis

#### PnL Calculation - Major Error Fixed
- **Fixed**: PnL now calculated as actual USDT amount instead of percentage only
- Previously ignored position size and leverage (e.g., 100 USDT @ 5% = 1000 USDT @ 5%)
- Now: `PnL (USDT) = Position Value × Price Change % × Leverage`
- Impact: Win rate, profit factor, and Sharpe ratio now accurate

#### Position Tracking - Missing Critical Data
- **Fixed**: Open position records now store quantity and leverage
- Previously only stored price and time
- Essential for accurate PnL calculations

#### Position Key Logic - Long/Short Conflict
- **Fixed**: Changed from `symbol` to `symbol_side` format
- Now properly distinguishes between long and short positions
- Example: `BTCUSDT_long` vs `BTCUSDT_short`

#### Sharpe Ratio Calculation - Code Optimization
- **Changed**: Replaced custom Newton's method with `math.Sqrt`
- More reliable, maintainable, and efficient

### Why This Matters
- Historical trade statistics now show real USDT profit/loss
- Performance comparison between different leverage trades is accurate
- AI self-learning mechanism receives correct feedback
- Multi-position tracking (long + short simultaneously) works correctly

---

## [2.0.2] - 2025-10-29

### Fixed - Aster Exchange Precision Error

- Fixed Aster exchange precision error (code -1111)
- Improved price and quantity formatting to match exchange requirements
- Added detailed precision processing logs for debugging
- Enhanced all order functions with proper precision handling

#### Technical Details
- Added `formatFloatWithPrecision` function
- Price and quantity formatted according to exchange specifications
- Trailing zeros removed to optimize API requests

---

## [2.0.1] - 2025-10-29

### Fixed - ComparisonChart Data Processing

- Fixed ComparisonChart data processing logic
- Switched from cycle_number to timestamp grouping
- Resolved chart freezing issue when backend restarts
- Improved chart data display (shows all historical data chronologically)
- Enhanced debugging logs

---

## [2.0.0] - 2025-10-28

### Added - Major Updates

- AI self-learning mechanism (historical feedback, performance analysis)
- Multi-trader competition mode (Qwen vs DeepSeek)
- Binance-style UI (complete interface imitation)
- Performance comparison charts (real-time ROI comparison)
- Risk control optimization (per-coin position limit adjustment)

### Fixed

- Fixed hardcoded initial balance issue
- Fixed multi-trader data sync issue
- Optimized chart data alignment (using cycle_number)

---

## [1.0.0] - 2025-10-27

### Added - Initial Release

- Basic AI trading functionality
- Decision logging system
- Simple Web interface
- Support for Binance Futures
- DeepSeek and Qwen AI model integration

---

## How to Use This Changelog

### For Users
- Check the [Unreleased] section for upcoming features
- Review version sections to understand what changed
- Follow migration guides for breaking changes

### For Contributors
When making changes, add them to the [Unreleased] section under appropriate categories:
- **Added** - New features
- **Changed** - Changes to existing functionality
- **Deprecated** - Features that will be removed
- **Removed** - Features that were removed
- **Fixed** - Bug fixes
- **Security** - Security fixes

When releasing a new version, move [Unreleased] items to a new version section with date.

---

## Links

- [Documentation](docs/README.md)
- [Contributing Guidelines](CONTRIBUTING.md)
- [Security Policy](SECURITY.md)
- [GitHub Repository](https://github.com/NoFxAiOS/nofx)

---

**Last Updated:** 2025-11-01


================================================
FILE: CHANGELOG.zh-CN.md
================================================
# 更新日志

NOFX 项目的所有重要更改都将记录在此文件中。

本文件格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。

**语言:** [English](CHANGELOG.md) | [中文](CHANGELOG.zh-CN.md)

---

## [未发布]

### 新增
- 多语言文档系统(英文/中文/俄语/乌克兰语)
- 完整的快速开始指南(Docker、自定义 API)
- 架构文档,包含系统设计细节
- 用户指南,包含 FAQ 和故障排除
- 社区文档,包含悬赏计划

### 变更
- 重组文档结构为逻辑分类
- 更新所有 README 文件,添加适当的导航链接

---

## [3.0.0] - 2025-10-30

### 新增 - 重大架构变革 🚀

**系统完全重新设计 - 基于 Web 的配置平台**

这是一个**重大破坏性更新**,将 NOFX 从基于静态配置的系统完全转变为现代化的 Web 交易平台。

#### 数据库驱动架构
- SQLite 集成,取代静态 JSON 配置
- 持久化存储,自动时间戳
- 外键关系和触发器确保数据一致性
- 为 AI 模型、交易所、交易员和系统配置分离表结构

#### 基于 Web 的配置界面
- 完整的 Web 配置管理(无需编辑 JSON)
- 通过 Web 界面设置 AI 模型(DeepSeek/Qwen API 密钥)
- 交易所管理(Binance/Hyperliquid 凭证)
- 动态创建交易员(结合任意 AI 模型和交易所)
- 实时控制(无需重启即可启动/停止交易员)

#### 灵活架构
- 关注点分离(AI 模型和交易所独立)
- 混合搭配能力(无限组合)
- 可扩展设计(支持无限交易员)
- 清洁起点(无默认交易员)

#### 增强的 API 层
- RESTful 设计,完整的 CRUD 操作
- 新端点:
  - `GET/PUT /api/models` - AI 模型配置
  - `GET/PUT /api/exchanges` - 交易所配置
  - `POST/DELETE /api/traders` - 交易员管理
  - `POST /api/traders/:id/start|stop` - 交易员控制
- 更新所有 API 端点文档

#### 现代化代码库
- 类型安全,适当分离配置类型
- 数据库抽象,使用预处理语句
- 全面的错误处理和验证
- 更好的代码组织(数据库、API、业务逻辑)

### 变更
- **破坏性变更**:不再使用旧的 `config.json` 文件
- 必须通过 Web 界面进行配置
- 设置更简单,用户体验更好
- 配置更改无需重启服务器

### 为什么重要
- 🎯 **用户体验**:配置和管理更容易
- 🔧 **灵活性**:创建 AI 模型和交易所的任意组合
- 📊 **可扩展性**:支持复杂的多交易员设置
- 🔒 **可靠性**:数据库确保数据持久性和一致性
- 🚀 **面向未来**:为高级功能奠定基础

---

## [2.0.2] - 2025-10-29

### 修复 - 关键错误修复:交易历史和性能分析

#### 盈亏计算 - 重大错误修复
- **修复**:盈亏现在计算为实际 USDT 金额,而不是仅百分比
- 之前忽略了仓位大小和杠杆(例如,100 USDT @ 5% = 1000 USDT @ 5%)
- 现在:`盈亏 (USDT) = 仓位价值 × 价格变化 % × 杠杆`
- 影响:胜率、盈利因子和夏普比率现在准确

#### 仓位跟踪 - 缺失关键数据
- **修复**:持仓记录现在存储数量和杠杆
- 之前只存储价格和时间
- 这对准确的盈亏计算至关重要

#### 仓位键逻辑 - 多空冲突
- **修复**:从 `symbol` 改为 `symbol_side` 格式
- 现在正确区分多头和空头仓位
- 示例:`BTCUSDT_long` vs `BTCUSDT_short`

#### 夏普比率计算 - 代码优化
- **变更**:用 `math.Sqrt` 替换自定义牛顿法
- 更可靠、可维护和高效

### 为什么重要
- 历史交易统计现在显示真实的 USDT 盈亏
- 不同杠杆交易之间的性能比较准确
- AI 自学习机制接收正确的反馈
- 多仓位跟踪(同时多空)正常工作

---

## [2.0.2] - 2025-10-29

### 修复 - Aster 交易所精度错误

- 修复 Aster 交易所精度错误(代码 -1111)
- 改进价格和数量格式化以匹配交易所要求
- 添加详细的精度处理日志用于调试
- 增强所有订单函数的精度处理

#### 技术细节
- 添加 `formatFloatWithPrecision` 函数
- 根据交易所规范格式化价格和数量
- 删除尾随零以优化 API 请求

---

## [2.0.1] - 2025-10-29

### 修复 - ComparisonChart 数据处理

- 修复 ComparisonChart 数据处理逻辑
- 从 cycle_number 切换到时间戳分组
- 解决后端重启时图表冻结问题
- 改进图表数据显示(按时间顺序显示所有历史数据)
- 增强调试日志

---

## [2.0.0] - 2025-10-28

### 新增 - 重大更新

- AI 自学习机制(历史反馈、性能分析)
- 多交易员竞赛模式(Qwen vs DeepSeek)
- 币安风格 UI(完整界面仿制)
- 性能比较图表(实时 ROI 比较)
- 风险控制优化(每币种仓位限制调整)

### 修复

- 修复硬编码初始余额问题
- 修复多交易员数据同步问题
- 优化图表数据对齐(使用 cycle_number)

---

## [1.0.0] - 2025-10-27

### 新增 - 初始版本

- 基础 AI 交易功能
- 决策日志系统
- 简单的 Web 界面
- 支持币安合约
- DeepSeek 和 Qwen AI 模型集成

---

## 如何使用本更新日志

### 用户
- 查看 [未发布] 部分了解即将推出的功能
- 查看版本部分了解变更内容
- 遵循破坏性变更的迁移指南

### 贡献者
进行更改时,将它们添加到 [未发布] 部分的相应类别下:
- **新增** - 新功能
- **变更** - 现有功能的变更
- **弃用** - 即将删除的功能
- **移除** - 已删除的功能
- **修复** - 错误修复
- **安全** - 安全修复

发布新版本时,将 [未发布] 项目移动到带日期的新版本部分。

---

## 链接

- [文档](docs/README.md)
- [贡献指南](CONTRIBUTING.md)
- [安全策略](SECURITY.md)
- [GitHub 仓库](https://github.com/NoFxAiOS/nofx)

---

**最后更新:** 2025-11-01


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct / 贡献者公约行为准则

**Languages:** [English](#english) | [中文](#中文)

---

# English

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
  community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or advances of
  any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
  without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at:


You can also report via:
- **Twitter:** DM to [@nofx_official](https://x.com/nofx_official)

All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series of
actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within the
community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].

Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].

For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

---

# 中文

## 我们的承诺

作为成员、贡献者和领导者,我们承诺使社区中的每个人都不受骚扰,无论其年龄、体型、明显或不明显的残疾、种族、性别特征、性别认同和表达、经验水平、教育程度、社会经济地位、国籍、个人外貌、种族、种姓、肤色、宗教或性认同和取向如何。

我们承诺以有助于开放、友好、多元、包容和健康社区的方式行事和互动。

## 我们的标准

有助于为我们的社区创造积极环境的行为示例包括:

* 对他人表现出同理心和善意
* 尊重不同的意见、观点和经验
* 给予并优雅地接受建设性反馈
* 接受责任并向受我们错误影响的人道歉,并从经验中学习
* 关注不仅对我们个人最好,而且对整个社区最好的事情

不可接受的行为示例包括:

* 使用性化的语言或图像,以及任何形式的性关注或性挑逗
* 挑衅、侮辱性或贬损性评论,以及人身或政治攻击
* 公开或私下骚扰
* 未经他人明确许可,发布他人的私人信息,如物理地址或电子邮件地址
* 在专业环境中可能被合理认为不适当的其他行为

## 执行责任

社区领导者负责阐明和执行我们可接受行为的标准,并将对他们认为不适当、威胁性、冒犯性或有害的任何行为采取适当和公平的纠正措施。

社区领导者有权利和责任删除、编辑或拒绝不符合本行为准则的评论、提交、代码、wiki 编辑、问题和其他贡献,并在适当时传达审核决定的原因。

## 范围

本行为准则适用于所有社区空间,也适用于个人在公共空间正式代表社区的情况。代表我们社区的示例包括使用官方电子邮件地址、通过官方社交媒体账户发布信息,或在线上或线下活动中担任指定代表。

## 执行

可以向负责执行的社区领导者报告滥用、骚扰或其他不可接受行为的实例:


您也可以通过以下方式报告:
- **Twitter:** 私信 [@nofx_official](https://x.com/nofx_official)

所有投诉都将得到迅速和公正的审查和调查。

所有社区领导者都有义务尊重任何事件报告者的隐私和安全。

## 执行指南

社区领导者将遵循这些社区影响指南来确定他们认为违反本行为准则的任何行动的后果:

### 1. 纠正

**社区影响**:使用不适当的语言或其他被认为在社区中不专业或不受欢迎的行为。

**后果**:社区领导者的私下书面警告,说明违规的性质和解释为什么行为不适当。可能要求公开道歉。

### 2. 警告

**社区影响**:通过单一事件或一系列行动违规。

**后果**:警告并说明持续行为的后果。在指定时间内不与相关人员互动,包括不主动与执行行为准则的人互动。这包括避免在社区空间以及外部渠道(如社交媒体)的互动。违反这些条款可能导致临时或永久禁令。

### 3. 临时禁令

**社区影响**:严重违反社区标准,包括持续的不当行为。

**后果**:在指定时间内临时禁止与社区进行任何形式的互动或公开交流。在此期间,不允许与相关人员进行公开或私下互动,包括不主动与执行行为准则的人互动。违反这些条款可能导致永久禁令。

### 4. 永久禁令

**社区影响**:表现出违反社区标准的模式,包括持续的不当行为、对个人的骚扰,或对个人类别的攻击或贬低。

**后果**:永久禁止在社区内进行任何形式的公开互动。

## 归属

本行为准则改编自 [贡献者公约][homepage] 2.1 版,可在
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1] 获取。

社区影响指南受到 [Mozilla 行为准则执行阶梯][Mozilla CoC] 的启发。

有关本行为准则的常见问题解答,请参阅 [https://www.contributor-covenant.org/faq][FAQ]。翻译版本可在 [https://www.contributor-covenant.org/translations][translations] 获取。

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations


================================================
FILE: CONTRIBUTING.md
================================================
# 🤝 Contributing to NOFX

**Language:** [English](CONTRIBUTING.md) | [中文](docs/i18n/zh-CN/CONTRIBUTING.md)

Thank you for your interest in contributing to NOFX! This document provides guidelines and workflows for contributing to the project.

---

## 📑 Table of Contents

- [Code of Conduct](#code-of-conduct)
- [How Can I Contribute?](#how-can-i-contribute)
- [Development Workflow](#development-workflow)
- [PR Submission Guidelines](#pr-submission-guidelines)
- [Coding Standards](#coding-standards)
- [Commit Message Guidelines](#commit-message-guidelines)
- [Review Process](#review-process)
- [Bounty Program](#bounty-program)

---

## 📜 Code of Conduct

This project adheres to the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code.

---

## 🎯 How Can I Contribute?

### 1. Report Bugs 🐛

- Use the [Bug Report Template](.github/ISSUE_TEMPLATE/bug_report.md)
- Check if the bug has already been reported
- Include detailed reproduction steps
- Provide environment information (OS, Go version, etc.)

### 2. Suggest Features ✨

- Use the [Feature Request Template](.github/ISSUE_TEMPLATE/feature_request.md)
- Explain the use case and benefits
- Check if it aligns with the [project roadmap](docs/roadmap/README.md)

### 3. Submit Pull Requests 🔧

Before submitting a PR, please check the following:

#### ✅ **Accepted Contributions**

**High Priority** (aligned with roadmap):
- 🔒 Security enhancements (encryption, authentication, RBAC)
- 🧠 AI model integrations (GPT-4, Claude, Gemini Pro)
- 🔗 Exchange integrations (OKX, Bybit, Lighter, EdgeX)
- 📊 Trading data APIs (AI500, OI analysis, NetFlow)
- 🎨 UI/UX improvements (mobile responsiveness, charts)
- ⚡ Performance optimizations
- 🐛 Bug fixes
- 📝 Documentation improvements

**Medium Priority:**
- ✅ Test coverage improvements
- 🌐 Internationalization (new language support)
- 🔧 Build/deployment tooling
- 📈 Monitoring and logging enhancements

#### ❌ **Not Accepted** (without prior discussion)

- Major architectural changes without RFC (Request for Comments)
- Features not aligned with project roadmap
- Breaking changes without migration path
- Code that introduces new dependencies without justification
- Experimental features without opt-in flag

**⚠️ Important:** For major features, please open an issue for discussion **before** starting work.

---

## 🛠️ Development Workflow

### 1. Fork and Clone

```bash
# Fork the repository on GitHub
# Then clone your fork
git clone https://github.com/YOUR_USERNAME/nofx.git
cd nofx

# Add upstream remote
git remote add upstream https://github.com/NoFxAiOS/nofx.git
```

### 2. Create a Feature Branch

```bash
# Update your local dev branch
git checkout dev
git pull upstream dev

# Create a new branch
git checkout -b feature/your-feature-name
# or
git checkout -b fix/your-bug-fix
```

**Branch Naming Convention:**
- `feature/` - New features
- `fix/` - Bug fixes
- `docs/` - Documentation updates
- `refactor/` - Code refactoring
- `perf/` - Performance improvements
- `test/` - Test updates
- `chore/` - Build/config changes

### 3. Set Up Development Environment

```bash
# Install Go dependencies
go mod download

# Install frontend dependencies
cd web
npm install
cd ..

# Install TA-Lib (required)
# macOS:
brew install ta-lib

# Ubuntu/Debian:
sudo apt-get install libta-lib0-dev
```

### 4. Make Your Changes

- Follow the [coding standards](#coding-standards)
- Write tests for new features
- Update documentation as needed
- Keep commits focused and atomic

### 5. Test Your Changes

```bash
# Run backend tests
go test ./...

# Build backend
go build -o nofx

# Run frontend in dev mode
cd web
npm run dev

# Build frontend
npm run build
```

### 6. Commit Your Changes

Follow the [commit message guidelines](#commit-message-guidelines):

```bash
git add .
git commit -m "feat: add support for OKX exchange integration"
```

### 7. Push and Create PR

```bash
# Push to your fork
git push origin feature/your-feature-name

# Go to GitHub and create a Pull Request
# Use the PR template and fill in all sections
```

---

## 📝 PR Submission Guidelines

### Before Submitting

- [ ] Code compiles successfully (`go build` and `npm run build`)
- [ ] All tests pass (`go test ./...`)
- [ ] No linting errors (`go fmt`, `go vet`)
- [ ] Documentation is updated
- [ ] Commits follow conventional commits format
- [ ] Branch is rebased on latest `dev`

### PR Title Format

Use [Conventional Commits](https://www.conventionalcommits.org/) format:

```
<type>(<scope>): <subject>

Examples:
feat(exchange): add OKX exchange integration
fix(trader): resolve position tracking bug
docs(readme): update installation instructions
perf(ai): optimize prompt generation
refactor(core): extract common exchange interface
```

**Types:**
- `feat` - New feature
- `fix` - Bug fix
- `docs` - Documentation
- `style` - Code style (formatting, no logic change)
- `refactor` - Code refactoring
- `perf` - Performance improvement
- `test` - Test updates
- `chore` - Build/config changes
- `ci` - CI/CD changes
- `security` - Security improvements

### PR Description

Use the [PR template](.github/PULL_REQUEST_TEMPLATE.md) and ensure:

1. **Clear description** of what and why
2. **Type of change** is marked
3. **Related issues** are linked
4. **Testing steps** are documented
5. **Screenshots** for UI changes
6. **All checkboxes** are completed

### PR Size

Keep PRs focused and reasonably sized:

- ✅ **Small PR** (< 300 lines): Ideal, fast review
- ⚠️ **Medium PR** (300-1000 lines): Acceptable, may take longer
- ❌ **Large PR** (> 1000 lines): Please break into smaller PRs

---

## 💻 Coding Standards

### Go Code

```go
// ✅ Good: Clear naming, proper error handling
func ConnectToExchange(apiKey, secret string) (*Exchange, error) {
    if apiKey == "" || secret == "" {
        return nil, fmt.Errorf("API credentials are required")
    }

    client, err := createClient(apiKey, secret)
    if err != nil {
        return nil, fmt.Errorf("failed to create client: %w", err)
    }

    return &Exchange{client: client}, nil
}

// ❌ Bad: Poor naming, no error handling
func ce(a, s string) *Exchange {
    c := createClient(a, s)
    return &Exchange{client: c}
}
```

**Best Practices:**
- Use meaningful variable names
- Handle all errors explicitly
- Add comments for complex logic
- Follow Go idioms and conventions
- Run `go fmt` before committing
- Use `go vet` and `golangci-lint`

### TypeScript/React Code

```typescript
// ✅ Good: Type-safe, clear naming
interface TraderConfig {
  id: string;
  exchange: 'binance' | 'hyperliquid' | 'aster';
  aiModel: string;
  enabled: boolean;
}

const TraderCard: React.FC<{ trader: TraderConfig }> = ({ trader }) => {
  const [isRunning, setIsRunning] = useState(false);

  const handleStart = async () => {
    try {
      await startTrader(trader.id);
      setIsRunning(true);
    } catch (error) {
      console.error('Failed to start trader:', error);
    }
  };

  return <div>...</div>;
};

// ❌ Bad: No types, unclear naming
const TC = (props) => {
  const [r, setR] = useState(false);
  const h = () => { startTrader(props.t.id); setR(true); };
  return <div>...</div>;
};
```

**Best Practices:**
- Use TypeScript strict mode
- Define interfaces for all data structures
- Avoid `any` type
- Use functional components with hooks
- Follow React best practices
- Run `npm run lint` before committing

### File Structure

```
NOFX/
├── cmd/               # Main applications
├── internal/          # Private code
│   ├── exchange/      # Exchange adapters
│   ├── trader/        # Trading logic
│   ├── ai/           # AI integrations
│   └── api/          # API handlers
├── pkg/              # Public libraries
├── web/              # Frontend
│   ├── src/
│   │   ├── components/
│   │   ├── pages/
│   │   ├── hooks/
│   │   └── utils/
│   └── public/
└── docs/             # Documentation
```

---

## 📋 Commit Message Guidelines

### Format

```
<type>(<scope>): <subject>

<body>

<footer>
```

### Examples

```
feat(exchange): add OKX futures API integration

- Implement order placement and cancellation
- Add balance and position retrieval
- Support leverage configuration

Closes #123
```

```
fix(trader): prevent duplicate position opening

The trader was opening multiple positions in the same direction
for the same symbol. Added check to prevent this behavior.

Fixes #456
```

```
docs: update Docker deployment guide

- Add troubleshooting section
- Update environment variables
- Add examples for common scenarios
```

### Rules

- Use present tense ("add" not "added")
- Use imperative mood ("move" not "moves")
- First line ≤ 72 characters
- Reference issues and PRs
- Explain "what" and "why", not "how"

---

## 🔍 Review Process

### Timeline

- **Initial review:** Within 2-3 business days
- **Follow-up reviews:** Within 1-2 business days
- **Bounty PRs:** Priority review within 1 business day

### Review Criteria

Reviewers will check:

1. **Functionality**
   - Does it work as intended?
   - Are edge cases handled?
   - No regression in existing features?

2. **Code Quality**
   - Follows coding standards?
   - Well-structured and readable?
   - Proper error handling?

3. **Testing**
   - Adequate test coverage?
   - Tests pass in CI?
   - Manual testing documented?

4. **Documentation**
   - Code comments where needed?
   - README/docs updated?
   - API changes documented?

5. **Security**
   - No hardcoded secrets?
   - Input validation?
   - No known vulnerabilities?

### Response to Feedback

- Address all review comments
- Ask questions if unclear
- Mark conversations as resolved
- Re-request review after changes

### Approval and Merge

- Requires **1 approval** from maintainers
- All CI checks must pass
- No unresolved conversations
- Maintainers will merge (squash merge for small PRs, merge commit for features)

---

## 💰 Bounty Program

### How It Works

1. Check [open bounty issues](https://github.com/NoFxAiOS/nofx/labels/bounty)
2. Comment to claim (first come, first served)
3. Complete work within deadline
4. Submit PR with bounty claim section filled
5. Get paid upon merge

### Guidelines

- Read [Bounty Guide](docs/community/bounty-guide.md)
- Meet all acceptance criteria
- Include demo video/screenshots
- Follow all contribution guidelines
- Payment details discussed privately

---

## ❓ Questions?

- **General questions:** Join our [Telegram Community](https://t.me/nofx_dev_community)
- **Technical questions:** Open a [Discussion](https://github.com/NoFxAiOS/nofx/discussions)
- **Security issues:** See [Security Policy](SECURITY.md)
- **Bug reports:** Use [Bug Report Template](.github/ISSUE_TEMPLATE/bug_report.md)

---

## 📚 Additional Resources

- [Project Roadmap](docs/roadmap/README.md)
- [Architecture Documentation](docs/architecture/README.md)
- [Deployment Guide](docs/getting-started/docker-deploy.en.md)

---

## 🙏 Thank You!

Your contributions make NOFX better for everyone. We appreciate your time and effort!

**Happy coding! 🚀**


================================================
FILE: DISCLAIMER.md
================================================
# ⚠️ Disclaimer / 免责声明

**Last Updated / 最后更新**: January 2025

---

## 🇬🇧 English Version

### Important Legal Disclaimer

**PLEASE READ THIS DISCLAIMER CAREFULLY BEFORE USING NOFX.**

By using NOFX (the "Software"), you acknowledge that you have read, understood, and agree to be bound by this disclaimer. If you do not agree with any part of this disclaimer, you should not use the Software.

### 1. No Financial Advice

NOFX is an **experimental software tool** for educational and research purposes only. Nothing contained in this Software should be construed as:

- ❌ Financial advice
- ❌ Investment advice
- ❌ Trading advice
- ❌ Legal advice
- ❌ Tax advice
- ❌ Any form of professional advice

**The Software does not provide, and should not be relied upon for, any form of financial, investment, legal, or tax advice.**

### 2. Experimental Nature

NOFX is an **experimental AI-powered trading system** that:

- ⚠️ Uses artificial intelligence which may make unpredictable decisions
- ⚠️ Is still under active development
- ⚠️ May contain bugs, errors, or vulnerabilities
- ⚠️ Has not been audited by professional financial advisors
- ⚠️ Has not been approved or endorsed by any financial regulatory authority

**USE AT YOUR OWN RISK.**

### 3. Trading Risks

Trading cryptocurrencies, stocks, futures, options, and other financial instruments carries **substantial risk** of loss and is not suitable for all investors. You should be aware of the following risks:

#### Market Risks
- 💸 You can lose **all or more than** your initial investment
- 📉 Markets are highly volatile and unpredictable
- 🌊 Sudden market movements can cause liquidation
- ⚡ "Flash crashes" can wipe out positions instantly
- 🌍 Global events can cause extreme volatility

#### Leverage Risks
- 🔥 Leverage amplifies both **gains and losses**
- ⚠️ Using high leverage can result in **rapid liquidation**
- 💥 Losses can exceed your initial deposit
- 🎲 Even small market moves can trigger margin calls

#### AI/Algorithm Risks
- 🤖 AI decisions are based on historical data and patterns
- 📊 Past performance does **not** guarantee future results
- 🔮 AI cannot predict black swan events
- 🐛 Software bugs may cause unexpected behavior
- ⏱️ System failures may prevent closing positions

#### Technical Risks
- 🌐 Internet connectivity issues
- 🔌 Exchange API downtime or rate limiting
- 💻 Hardware or software failures
- 🔒 Security breaches or hacks
- ⚡ Network latency causing slippage

#### Exchange Risks
- 🏦 Exchange insolvency or bankruptcy
- 🚫 Account freezing or restrictions
- 💰 Withdrawal delays or limits
- 🔐 Exchange hacks or security breaches
- ⚖️ Regulatory actions against exchanges

### 4. No Guarantees or Warranties

THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO:

- ❌ No warranty of merchantability
- ❌ No warranty of fitness for a particular purpose
- ❌ No warranty of non-infringement
- ❌ No warranty of accuracy or reliability
- ❌ No warranty of profitability
- ❌ No warranty of uptime or availability

**We make no guarantees about:**
- The Software's performance
- The accuracy of AI decisions
- The profitability of trades
- The safety of your funds
- The availability of the service

### 5. Limitation of Liability

TO THE MAXIMUM EXTENT PERMITTED BY LAW:

- 🚫 The developers and contributors of NOFX shall **NOT** be liable for any damages arising from the use of this Software
- 🚫 This includes but is not limited to: direct, indirect, incidental, special, consequential, or punitive damages
- 🚫 This includes loss of profits, loss of data, loss of funds, or any other commercial damages or losses

**YOU ASSUME ALL RISK AND LIABILITY** for your use of the Software.

### 6. User Responsibility

By using NOFX, you acknowledge and agree that:

- ✅ You are solely responsible for your trading decisions
- ✅ You are solely responsible for securing your API keys and private keys
- ✅ You are solely responsible for understanding the risks involved
- ✅ You are solely responsible for complying with applicable laws and regulations
- ✅ You are solely responsible for any tax obligations arising from your trading
- ✅ You will not hold the developers liable for any losses

### 7. Regulatory Compliance

- ⚖️ You are responsible for ensuring your use of the Software complies with all applicable laws and regulations in your jurisdiction
- 🌍 Cryptocurrency trading may be restricted or prohibited in certain jurisdictions
- 📋 You may need to register with financial authorities or obtain licenses
- 💵 You are responsible for reporting and paying any applicable taxes

**Using NOFX does not guarantee regulatory compliance.**

### 8. No Professional Relationship

- 🚫 No attorney-client relationship is created
- 🚫 No accountant-client relationship is created
- 🚫 No financial advisor-client relationship is created
- 🚫 No broker-client relationship is created

**You should consult with qualified professionals** before making financial decisions.

### 9. Testing and Paper Trading Recommended

We **strongly recommend** that you:

- ✅ Start with paper trading or testnet environments
- ✅ Test with small amounts you can afford to lose
- ✅ Thoroughly understand the system before live trading
- ✅ Monitor the system regularly
- ✅ Have a plan to handle emergencies

**Never invest more than you can afford to lose.**

### 10. Open Source Software

NOFX is open source software licensed under AGPL-3.0:

- 📖 You can review the source code
- 🔧 You can modify the code at your own risk
- 🤝 Contributions are welcome
- ⚖️ You must comply with the license terms

**Using or modifying the Software is at your own risk.**

### 11. Third-Party Services

NOFX integrates with third-party services (exchanges, AI APIs):

- 🔗 We are not responsible for third-party service outages
- 🔗 We are not responsible for third-party data accuracy
- 🔗 We are not responsible for third-party security breaches
- 🔗 You should review the terms of service of all third-party providers

### 12. Changes to Disclaimer

This disclaimer may be updated at any time without notice. Continued use of the Software after changes constitutes acceptance of the new disclaimer.

### 13. Severability

If any provision of this disclaimer is found to be unenforceable or invalid, that provision will be limited or eliminated to the minimum extent necessary so that this disclaimer will otherwise remain in full force and effect.

### 14. Contact for Legal Matters

For legal inquiries, contact:
- **Email**: tinklefund@gmail.com
- **Twitter**: [@Web3Tinkle](https://x.com/Web3Tinkle)

---

## 🇨🇳 中文版本

### 重要法律免责声明

**使用NOFX前请仔细阅读本免责声明。**

使用NOFX("本软件")即表示您已阅读、理解并同意受本免责声明约束。如果您不同意本免责声明的任何部分,请勿使用本软件。

### 1. 非财务建议

NOFX是一个**实验性软件工具**,仅用于教育和研究目的。本软件中的任何内容都不应被解释为:

- ❌ 财务建议
- ❌ 投资建议
- ❌ 交易建议
- ❌ 法律建议
- ❌ 税务建议
- ❌ 任何形式的专业建议

**本软件不提供也不应依赖任何形式的财务、投资、法律或税务建议。**

### 2. 实验性质

NOFX是一个**实验性的AI驱动交易系统**:

- ⚠️ 使用可能做出不可预测决策的人工智能
- ⚠️ 仍在积极开发中
- ⚠️ 可能包含错误、缺陷或漏洞
- ⚠️ 未经专业财务顾问审核
- ⚠️ 未获任何金融监管机构批准或背书

**使用风险自负。**

### 3. 交易风险

交易加密货币、股票、期货、期权和其他金融工具具有**重大亏损风险**,不适合所有投资者。您应该了解以下风险:

#### 市场风险
- 💸 您可能**损失全部或超过**初始投资
- 📉 市场高度波动且不可预测
- 🌊 突然的市场变动可能导致清算
- ⚡ "闪崩"可能瞬间清空仓位
- 🌍 全球事件可能引发极端波动

#### 杠杆风险
- 🔥 杠杆同时放大**收益和损失**
- ⚠️ 使用高杠杆可能导致**快速清算**
- 💥 损失可能超过您的初始存款
- 🎲 即使是小幅市场波动也可能触发追加保证金

#### AI/算法风险
- 🤖 AI决策基于历史数据和模式
- 📊 过去的表现**不能**保证未来的结果
- 🔮 AI无法预测黑天鹅事件
- 🐛 软件错误可能导致意外行为
- ⏱️ 系统故障可能阻止平仓

#### 技术风险
- 🌐 互联网连接问题
- 🔌 交易所API停机或速率限制
- 💻 硬件或软件故障
- 🔒 安全漏洞或黑客攻击
- ⚡ 网络延迟导致滑点

#### 交易所风险
- 🏦 交易所破产或倒闭
- 🚫 账户冻结或限制
- 💰 提款延迟或限额
- 🔐 交易所被黑客攻击
- ⚖️ 针对交易所的监管行动

### 4. 不提供保证或担保

本软件按"现状"提供,不提供任何明示或暗示的保证,包括但不限于:

- ❌ 不保证适销性
- ❌ 不保证适合特定用途
- ❌ 不保证不侵权
- ❌ 不保证准确性或可靠性
- ❌ 不保证盈利性
- ❌ 不保证正常运行时间或可用性

**我们不保证:**
- 软件的性能
- AI决策的准确性
- 交易的盈利性
- 您的资金安全
- 服务的可用性

### 5. 责任限制

在法律允许的最大范围内:

- 🚫 NOFX的开发者和贡献者**不对**因使用本软件而产生的任何损害承担责任
- 🚫 这包括但不限于:直接、间接、偶然、特殊、后果性或惩罚性损害
- 🚫 这包括利润损失、数据损失、资金损失或任何其他商业损害或损失

**您承担使用本软件的所有风险和责任。**

### 6. 用户责任

使用NOFX即表示您承认并同意:

- ✅ 您对自己的交易决策承担全部责任
- ✅ 您对保护API密钥和私钥承担全部责任
- ✅ 您对理解所涉及的风险承担全部责任
- ✅ 您对遵守适用的法律法规承担全部责任
- ✅ 您对交易产生的任何税务义务承担全部责任
- ✅ 您不会因任何损失追究开发者的责任

### 7. 合规性

- ⚖️ 您有责任确保使用本软件符合您所在司法管辖区的所有适用法律法规
- 🌍 加密货币交易在某些司法管辖区可能受到限制或禁止
- 📋 您可能需要向金融机构注册或获得许可
- 💵 您有责任申报和缴纳任何适用的税款

**使用NOFX并不保证合规性。**

### 8. 无专业关系

- 🚫 不构成律师-客户关系
- 🚫 不构成会计师-客户关系
- 🚫 不构成财务顾问-客户关系
- 🚫 不构成经纪人-客户关系

**您应在做出财务决策前咨询合格的专业人士。**

### 9. 建议测试和模拟交易

我们**强烈建议**您:

- ✅ 从模拟交易或测试网环境开始
- ✅ 用您可以承受损失的小额资金测试
- ✅ 在实盘交易前充分了解系统
- ✅ 定期监控系统
- ✅ 制定应急计划

**永远不要投资超过您能承受损失的金额。**

### 10. 开源软件

NOFX是根据AGPL-3.0许可的开源软件:

- 📖 您可以查看源代码
- 🔧 您可以自行修改代码,风险自负
- 🤝 欢迎贡献
- ⚖️ 您必须遵守许可条款

**使用或修改软件的风险由您自行承担。**

### 11. 第三方服务

NOFX集成了第三方服务(交易所、AI API):

- 🔗 我们不对第三方服务中断负责
- 🔗 我们不对第三方数据准确性负责
- 🔗 我们不对第三方安全漏洞负责
- 🔗 您应查看所有第三方提供商的服务条款

### 12. 免责声明变更

本免责声明可能随时更新,恕不另行通知。在更改后继续使用软件即表示接受新的免责声明。

### 13. 可分割性

如果本免责声明的任何条款被认定为不可执行或无效,该条款将被限制或删除到最小必要程度,以使本免责声明的其余部分保持完全有效。

### 14. 法律事务联系方式


---

## ⚖️ Summary / 总结

### In Simple Terms / 简而言之:

1. 🎓 **Educational Tool Only** / **仅供教育使用**
   - This is a learning and research tool, not professional trading software

2. ⚠️ **High Risk** / **高风险**
   - You can lose all your money. Trading is extremely risky.

3. 🤖 **Experimental AI** / **实验性AI**
   - The AI may make mistakes. Do not blindly trust it.

4. 🚫 **No Guarantees** / **无保证**
   - No promises of profit, accuracy, or reliability.

5. 🙋 **Your Responsibility** / **您的责任**
   - You are 100% responsible for all trading decisions and outcomes.

6. 💡 **Test First** / **先测试**
   - Always test with small amounts or paper trading first.

7. 📚 **Do Your Own Research** / **做好研究**
   - Consult professionals. Understand the risks. Never invest what you can't lose.

---

## 📞 Questions?

If you have questions about this disclaimer:
- Read the [Security Policy](.github/SECURITY.md)
- Read the [Contributing Guidelines](CONTRIBUTING.md)
- Join our [Telegram Community](https://t.me/nofx_dev_community)
- Contact via [Twitter](https://x.com/nofx_official)

---

**BY USING NOFX, YOU ACKNOWLEDGE THAT YOU HAVE READ, UNDERSTOOD, AND AGREE TO THIS DISCLAIMER.**

**使用NOFX即表示您已阅读、理解并同意本免责声明。**


================================================
FILE: Dockerfile.railway
================================================
# Railway All-in-One: Reuse existing GHCR images
# Extract content from existing images and merge into a single container

# Extract binary from backend image
FROM ghcr.io/nofxaios/nofx/nofx-backend:latest AS backend

# Extract static files from frontend image
FROM ghcr.io/nofxaios/nofx/nofx-frontend:latest AS frontend

# Final image
FROM alpine:latest

RUN apk add --no-cache ca-certificates tzdata sqlite nginx openssl gettext

# Copy backend binary
COPY --from=backend /app/nofx /app/nofx

# Copy TA-Lib libraries
COPY --from=backend /usr/local/lib/libta_lib* /usr/local/lib/
RUN ldconfig /usr/local/lib 2>/dev/null || true

# Copy frontend static files
COPY --from=frontend /usr/share/nginx/html /usr/share/nginx/html

WORKDIR /app
RUN mkdir -p /app/data

# Startup script (includes nginx config generation)
COPY railway/start.sh /app/start.sh
RUN chmod +x /app/start.sh

ENV DB_PATH=/app/data/data.db

# Railway automatically sets the PORT environment variable
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:${PORT:-8080}/health || exit 1

CMD ["/app/start.sh"]


================================================
FILE: ENCRYPTION_README.md
================================================
# 🔐 End-to-End Encryption System

## Quick Start (5 Minutes)

```bash
# 1. Deploy encryption system
./deploy_encryption.sh

# 2. Restart application
go run main.go
```

## What's Changed?

### New Files
- `crypto/` - Core encryption modules
- `api/crypto_handler.go` - Encryption API endpoints
- `web/src/lib/crypto.ts` - Frontend encryption module
- `scripts/migrate_encryption.go` - Data migration tool
- `deploy_encryption.sh` - One-click deployment script

### Modified Files
None (backward compatible, no breaking changes)

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│              Three-Layer Security                        │
├─────────────────────────────────────────────────────────┤
│  Frontend: Two-stage input + clipboard obfuscation      │
│  Transport: RSA-4096 + AES-256-GCM encryption           │
│  Storage: Database encryption + audit logs              │
└─────────────────────────────────────────────────────────┘
```

## Integration

### 1. Initialize Encryption Manager (main.go)

```go
import "nofx/crypto"

func main() {
    // Initialize secure storage
    secureStorage, err := crypto.NewSecureStorage(db.GetDB())
    if err != nil {
        log.Fatalf("Encryption init failed: %v", err)
    }

    // Migrate existing data (optional, one-time)
    secureStorage.MigrateToEncrypted()

    // Register API routes
    cryptoHandler, _ := api.NewCryptoHandler(secureStorage)
    http.HandleFunc("/api/crypto/public-key", cryptoHandler.HandleGetPublicKey)

    // ... rest of your code
}
```

### 2. Frontend Integration

```typescript
import { twoStagePrivateKeyInput, fetchServerPublicKey } from '../lib/crypto';

// When saving exchange config
const serverPublicKey = await fetchServerPublicKey();
const { encryptedKey } = await twoStagePrivateKeyInput(serverPublicKey);

// Send encrypted data to backend
await api.post('/api/exchange/config', {
    encrypted_key: encryptedKey,
});
```

## Features

- ✅ **Zero Breaking Changes**: Backward compatible with existing data
- ✅ **Automatic Migration**: Old data automatically encrypted on first access
- ✅ **Audit Logs**: Complete tracking of all key operations
- ✅ **Key Rotation**: Built-in mechanism for periodic key updates
- ✅ **Performance**: <25ms overhead per operation

## Security Improvements

| Before | After | Improvement |
|--------|-------|-------------|
| Plaintext in DB | AES-256 encrypted | ∞ |
| Clipboard sniffing | Obfuscated | 90%+ |
| Browser extension theft | End-to-end encrypted | 99% |
| Server breach | Requires key theft | 80% |

## Testing

```bash
# Run encryption tests
go test ./crypto -v

# Expected output:
# ✅ RSA key pair generation
# ✅ AES encryption/decryption
# ✅ Hybrid encryption
```

## Cost

- **Development**: 0 (implemented)
- **Runtime**: <0.1ms per operation
- **Storage**: +30% (encrypted data size)
- **Maintenance**: Minimal (automated)

## Rollback

If needed, rollback is simple:

```bash
# Restore backup
cp data.db.backup data.db

# Comment out 3 lines in main.go
# (encryption initialization)

# Restart
go run main.go
```

## Support

- **Documentation**: See inline code comments
- **Issues**: Report via GitHub issues
- **Questions**: Check `crypto/encryption_test.go` for examples

---

**No configuration required. Just deploy and it works.**


================================================
FILE: LICENSE
================================================
                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 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.

                            Preamble

  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 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 
Download .txt
gitextract_lfraba5v/

├── .dockerignore
├── .github/
│   ├── CLA.md
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bounty_claim.md
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PR_TITLE_GUIDE.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   ├── README.md
│   │   ├── backend.md
│   │   ├── docs.md
│   │   ├── frontend.md
│   │   └── general.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── SECURITY.md
│   ├── labeler.yml
│   ├── labels.yml
│   └── workflows/
│       ├── README.md
│       ├── docker-build.yml
│       ├── pr-checks-comment.yml
│       ├── pr-checks-run.yml
│       ├── pr-checks.yml
│       ├── pr-docker-check.yml
│       ├── pr-docker-compose-healthcheck.yml
│       ├── pr-go-test-coverage.yml
│       ├── pr-template-suggester.yml
│       ├── scripts/
│       │   ├── calculate_coverage.py
│       │   ├── comment_pr.py
│       │   └── requirements.txt
│       └── test.yml
├── .gitignore
├── .husky/
│   ├── _/
│   │   └── husky.sh
│   └── pre-commit
├── CHANGELOG.md
├── CHANGELOG.zh-CN.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DISCLAIMER.md
├── Dockerfile.railway
├── ENCRYPTION_README.md
├── LICENSE
├── Makefile
├── README.ja.md
├── README.md
├── SECURITY.md
├── api/
│   ├── crypto_handler.go
│   ├── errors.go
│   ├── handler_ai_model.go
│   ├── handler_competition.go
│   ├── handler_exchange.go
│   ├── handler_klines.go
│   ├── handler_order.go
│   ├── handler_telegram.go
│   ├── handler_trader.go
│   ├── handler_trader_config.go
│   ├── handler_trader_status.go
│   ├── handler_user.go
│   ├── handler_wallet.go
│   ├── route_registry.go
│   ├── server.go
│   ├── server_test.go
│   ├── strategy.go
│   ├── traderid_test.go
│   ├── utils.go
│   └── utils_test.go
├── auth/
│   └── auth.go
├── config/
│   └── config.go
├── crypto/
│   └── crypto.go
├── docker/
│   ├── Dockerfile.backend
│   └── Dockerfile.frontend
├── docker-compose.prod.yml
├── docker-compose.stable.yml
├── docker-compose.yml
├── docs/
│   ├── Git工作流规范.md
│   ├── MIGRATION_GUIDE.md
│   ├── README.md
│   ├── api/
│   │   └── API_REFERENCE.md
│   ├── architecture/
│   │   ├── README.md
│   │   ├── README.zh-CN.md
│   │   ├── STRATEGY_MODULE.md
│   │   ├── STRATEGY_MODULE.zh-CN.md
│   │   └── X402_STREAMING_PAYMENT.md
│   ├── community/
│   │   ├── HOW_TO_MIGRATE_YOUR_PR.md
│   │   ├── HOW_TO_MIGRATE_YOUR_PR.zh-CN.md
│   │   ├── MIGRATION_ANNOUNCEMENT.md
│   │   ├── MIGRATION_ANNOUNCEMENT.zh-CN.md
│   │   ├── OFFICIAL_ACCOUNTS.md
│   │   ├── PR_COMMENT_TEMPLATE.md
│   │   ├── README.md
│   │   ├── bounty-aster.md
│   │   ├── bounty-guide.md
│   │   └── bounty-hyperliquid.md
│   ├── getting-started/
│   │   ├── README.md
│   │   ├── README.zh-CN.md
│   │   ├── aster-api-wallet.md
│   │   ├── binance-api.md
│   │   ├── blockrun-base-wallet.md
│   │   ├── blockrun-sol-wallet.md
│   │   ├── bybit-api.md
│   │   ├── custom-api.en.md
│   │   ├── custom-api.md
│   │   ├── hyperliquid-agent-wallet.md
│   │   ├── lighter-agent-wallet.md
│   │   └── okx-api.md
│   ├── guides/
│   │   ├── README.md
│   │   ├── README.zh-CN.md
│   │   ├── TROUBLESHOOTING.md
│   │   ├── TROUBLESHOOTING.zh-CN.md
│   │   ├── faq.en.md
│   │   └── faq.zh-CN.md
│   ├── i18n/
│   │   ├── README.md
│   │   ├── en/
│   │   │   ├── PRIVACY POLICY.md
│   │   │   └── TERMS OF SERVICE.md
│   │   ├── ja/
│   │   │   ├── PRIVACY POLICY.md
│   │   │   ├── README.md
│   │   │   └── TERMS OF SERVICE.md
│   │   ├── ko/
│   │   │   └── README.md
│   │   ├── ru/
│   │   │   ├── PRIVACY POLICY.md
│   │   │   ├── README.md
│   │   │   └── TERMS OF SERVICE.md
│   │   ├── uk/
│   │   │   ├── PRIVACY POLICY.md
│   │   │   ├── README.md
│   │   │   └── TERMS OF SERVICE.md
│   │   ├── vi/
│   │   │   └── README.md
│   │   └── zh-CN/
│   │       ├── CONTRIBUTING.md
│   │       ├── PRIVACY POLICY.md
│   │       ├── README.md
│   │       └── TERMS OF SERVICE.md
│   ├── legal/
│   │   ├── AGPL-VIOLATION-REPORT-ChainOpera-EN.md
│   │   └── AGPL-VIOLATION-REPORT-ChainOpera.md
│   ├── maintainers/
│   │   ├── PROJECT_MANAGEMENT.md
│   │   ├── PROJECT_MANAGEMENT.zh-CN.md
│   │   ├── PR_REVIEW_GUIDE.md
│   │   ├── PR_REVIEW_GUIDE.zh-CN.md
│   │   ├── README.md
│   │   ├── README.zh-CN.md
│   │   ├── SETUP_GUIDE.md
│   │   └── SETUP_GUIDE.zh-CN.md
│   ├── market-regime-classification-en.md
│   ├── market-regime-classification-zh.md
│   ├── plans/
│   │   ├── 2026-01-14-grid-trading-fixes.md
│   │   ├── 2026-01-17-grid-market-regime-design.md
│   │   ├── 2026-01-17-grid-market-regime-impl.md
│   │   ├── 2026-03-06-telegram-agent-redesign.md
│   │   └── 2026-03-06-telegram-bot.md
│   ├── pnl.md
│   ├── prompt-guide.md
│   ├── prompt-guide.zh-CN.md
│   ├── research/
│   │   └── AI-Trader-Analysis-Report.md
│   └── roadmap/
│       ├── README.md
│       └── README.zh-CN.md
├── go.mod
├── go.sum
├── hook/
│   ├── README.md
│   ├── hooks.go
│   ├── http_client_hook.go
│   ├── ip_hook.go
│   └── trader_hook.go
├── install-stable.sh
├── install.sh
├── kernel/
│   ├── engine.go
│   ├── engine_analysis.go
│   ├── engine_position.go
│   ├── engine_prompt.go
│   ├── formatter.go
│   ├── grid_engine.go
│   ├── prompt_builder.go
│   ├── prompt_builder_test.go
│   ├── schema.go
│   └── validate_test.go
├── logger/
│   ├── config.go
│   └── logger.go
├── main.go
├── manager/
│   └── trader_manager.go
├── market/
│   ├── api_client.go
│   ├── data.go
│   ├── data_indicators.go
│   ├── data_klines.go
│   ├── data_test.go
│   ├── historical.go
│   ├── timeframe.go
│   └── types.go
├── mcp/
│   ├── client.go
│   ├── client_test.go
│   ├── config.go
│   ├── config_usage_test.go
│   ├── context_guard.go
│   ├── context_guard_test.go
│   ├── examples_test.go
│   ├── hooks.go
│   ├── interface.go
│   ├── intro/
│   │   ├── BUILDER_EXAMPLES.md
│   │   ├── BUILDER_PATTERN_BENEFITS.md
│   │   ├── LOGRUS_INTEGRATION.md
│   │   ├── MIGRATION_GUIDE.md
│   │   └── README.md
│   ├── logger.go
│   ├── mock_test.go
│   ├── options.go
│   ├── options_test.go
│   ├── payment/
│   │   ├── blockrun_base.go
│   │   ├── blockrun_sol.go
│   │   ├── claw402.go
│   │   └── x402.go
│   ├── provider/
│   │   ├── claude.go
│   │   ├── deepseek.go
│   │   ├── gemini.go
│   │   ├── grok.go
│   │   ├── kimi.go
│   │   ├── minimax.go
│   │   ├── openai.go
│   │   ├── options_test.go
│   │   └── qwen.go
│   ├── providers.go
│   ├── registry.go
│   ├── request.go
│   ├── request_builder.go
│   └── request_builder_test.go
├── nginx/
│   └── nginx.conf
├── provider/
│   ├── alpaca/
│   │   └── kline.go
│   ├── coinank/
│   │   ├── base_coin.go
│   │   ├── coinank_api/
│   │   │   ├── base_coin.go
│   │   │   ├── base_coin_test.go
│   │   │   ├── depth_ws.go
│   │   │   ├── depth_ws_test.go
│   │   │   ├── kline.go
│   │   │   ├── kline_test.go
│   │   │   ├── kline_ws.go
│   │   │   └── kline_ws_test.go
│   │   ├── coinank_enum/
│   │   │   ├── exchange.go
│   │   │   ├── instrument_agg_sort_by.go
│   │   │   ├── interval.go
│   │   │   ├── product_type.go
│   │   │   ├── side.go
│   │   │   ├── sort_type.go
│   │   │   └── url.go
│   │   ├── coinank_http.go
│   │   ├── instrument_agg_rank.go
│   │   ├── instruments.go
│   │   ├── kline.go
│   │   ├── liquidation.go
│   │   ├── net_positions.go
│   │   └── open_interest.go
│   ├── hyperliquid/
│   │   ├── coins.go
│   │   ├── kline.go
│   │   └── kline_test.go
│   ├── nofxos/
│   │   ├── ai500.go
│   │   ├── client.go
│   │   ├── coin.go
│   │   ├── netflow.go
│   │   ├── oi.go
│   │   ├── price.go
│   │   └── util.go
│   └── twelvedata/
│       └── kline.go
├── railway/
│   └── start.sh
├── railway.toml
├── security/
│   └── url_validator.go
├── start.sh
├── store/
│   ├── ai_model.go
│   ├── decision.go
│   ├── driver.go
│   ├── equity.go
│   ├── exchange.go
│   ├── gorm.go
│   ├── grid.go
│   ├── order.go
│   ├── position.go
│   ├── position_builder.go
│   ├── position_history.go
│   ├── position_query.go
│   ├── store.go
│   ├── strategy.go
│   ├── telegram_config.go
│   ├── trader.go
│   └── user.go
├── telegram/
│   ├── agent/
│   │   ├── agent.go
│   │   ├── agent_test.go
│   │   ├── apicall.go
│   │   ├── manager.go
│   │   └── prompt.go
│   ├── bot.go
│   └── session/
│       └── memory.go
├── telemetry/
│   └── experience.go
├── trader/
│   ├── aster/
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   ├── trader_positions.go
│   │   ├── trader_sync.go
│   │   └── trader_test.go
│   ├── auto_trader.go
│   ├── auto_trader_decision.go
│   ├── auto_trader_grid.go
│   ├── auto_trader_grid_levels.go
│   ├── auto_trader_grid_orders.go
│   ├── auto_trader_grid_regime.go
│   ├── auto_trader_loop.go
│   ├── auto_trader_orders.go
│   ├── auto_trader_risk.go
│   ├── binance/
│   │   ├── futures.go
│   │   ├── futures_account.go
│   │   ├── futures_orders.go
│   │   ├── futures_positions.go
│   │   ├── futures_test.go
│   │   ├── order_sync.go
│   │   ├── order_sync_test.go
│   │   ├── sync_e2e_test.go
│   │   └── sync_verify_test.go
│   ├── bitget/
│   │   ├── order_sync.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_positions.go
│   ├── bybit/
│   │   ├── order_sync.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_positions.go
│   ├── exchange_sync_test.go
│   ├── gate/
│   │   ├── order_sync.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_test.go
│   ├── grid_regime.go
│   ├── grid_regime_test.go
│   ├── helpers.go
│   ├── hyperliquid/
│   │   ├── order_sync.go
│   │   ├── sync_test.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   ├── trader_positions.go
│   │   ├── trader_race_test.go
│   │   └── trader_sync.go
│   ├── indodax/
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_test.go
│   ├── interface.go
│   ├── kucoin/
│   │   ├── order_sync.go
│   │   ├── order_sync_test.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_positions.go
│   ├── lighter/
│   │   ├── account.go
│   │   ├── integration_test.go
│   │   ├── order_sync.go
│   │   ├── orders.go
│   │   ├── orders_test.go
│   │   ├── trader.go
│   │   ├── trading.go
│   │   └── types.go
│   ├── okx/
│   │   ├── order_sync.go
│   │   ├── trader.go
│   │   ├── trader_account.go
│   │   ├── trader_orders.go
│   │   └── trader_positions.go
│   ├── position_rebuild.go
│   ├── position_snapshot.go
│   ├── testutil/
│   │   └── test_suite.go
│   └── types/
│       └── interface.go
└── web/
    ├── .dockerignore
    ├── .husky/
    │   └── pre-commit
    ├── .prettierignore
    ├── .prettierrc.json
    ├── CHANGELOG.md
    ├── README.md
    ├── eslint.config.js
    ├── index.html
    ├── package.json
    ├── postcss.config.js
    ├── src/
    │   ├── App.tsx
    │   ├── components/
    │   │   ├── auth/
    │   │   │   ├── LoginPage.tsx
    │   │   │   ├── LoginRequiredOverlay.tsx
    │   │   │   ├── RegisterPage.test.tsx
    │   │   │   ├── RegisterPage.tsx
    │   │   │   ├── RegistrationDisabled.test.tsx
    │   │   │   ├── RegistrationDisabled.tsx
    │   │   │   └── ResetPasswordPage.tsx
    │   │   ├── charts/
    │   │   │   ├── AdvancedChart.tsx
    │   │   │   ├── ChartTabs.tsx
    │   │   │   ├── ChartWithOrders.tsx
    │   │   │   ├── ChartWithOrdersSimple.tsx
    │   │   │   ├── ComparisonChart.tsx
    │   │   │   ├── EquityChart.tsx
    │   │   │   └── TradingViewChart.tsx
    │   │   ├── common/
    │   │   │   ├── ConfirmDialog.tsx
    │   │   │   ├── Container.tsx
    │   │   │   ├── DeepVoidBackground.tsx
    │   │   │   ├── ExchangeIcons.tsx
    │   │   │   ├── Header.tsx
    │   │   │   ├── HeaderBar.tsx
    │   │   │   ├── MetricTooltip.tsx
    │   │   │   ├── ModelIcons.tsx
    │   │   │   ├── PunkAvatar.tsx
    │   │   │   ├── WebCryptoEnvironmentCheck.tsx
    │   │   │   └── WhitelistFullPage.tsx
    │   │   ├── faq/
    │   │   │   ├── FAQContent.tsx
    │   │   │   ├── FAQLayout.tsx
    │   │   │   ├── FAQSearchBar.tsx
    │   │   │   └── FAQSidebar.tsx
    │   │   ├── landing/
    │   │   │   ├── AboutSection.tsx
    │   │   │   ├── AnimatedSection.tsx
    │   │   │   ├── CommunitySection.tsx
    │   │   │   ├── FeaturesSection.tsx
    │   │   │   ├── FooterSection.tsx
    │   │   │   ├── HeroSection.tsx
    │   │   │   ├── HowItWorksSection.tsx
    │   │   │   ├── LoginModal.tsx
    │   │   │   ├── brand/
    │   │   │   │   ├── AgentTerminal.tsx
    │   │   │   │   ├── BrandFeatures.tsx
    │   │   │   │   ├── BrandHero.tsx
    │   │   │   │   ├── BrandStats.tsx
    │   │   │   │   └── Marquee.tsx
    │   │   │   └── core/
    │   │   │       ├── AgentGrid.tsx
    │   │   │       ├── DeploymentHub.tsx
    │   │   │       ├── LiveFeed.tsx
    │   │   │       └── TerminalHero.tsx
    │   │   ├── modals/
    │   │   │   ├── SetupPage.tsx
    │   │   │   └── TwoStageKeyModal.tsx
    │   │   ├── strategy/
    │   │   │   ├── CoinSourceEditor.tsx
    │   │   │   ├── GridConfigEditor.tsx
    │   │   │   ├── GridRiskPanel.tsx
    │   │   │   ├── IndicatorEditor.tsx
    │   │   │   ├── PromptSectionsEditor.tsx
    │   │   │   ├── PublishSettingsEditor.tsx
    │   │   │   └── RiskControlEditor.tsx
    │   │   ├── trader/
    │   │   │   ├── AITradersPage.tsx
    │   │   │   ├── CompetitionPage.test.tsx
    │   │   │   ├── CompetitionPage.tsx
    │   │   │   ├── ConfigStatusGrid.tsx
    │   │   │   ├── DecisionCard.tsx
    │   │   │   ├── ExchangeConfigModal.tsx
    │   │   │   ├── ModelCard.tsx
    │   │   │   ├── ModelConfigModal.tsx
    │   │   │   ├── ModelStepIndicator.tsx
    │   │   │   ├── PositionHistory.tsx
    │   │   │   ├── TelegramConfigModal.tsx
    │   │   │   ├── Tooltip.tsx
    │   │   │   ├── TraderConfigModal.tsx
    │   │   │   ├── TraderConfigViewModal.tsx
    │   │   │   ├── TradersList.tsx
    │   │   │   ├── model-constants.ts
    │   │   │   └── utils.ts
    │   │   └── ui/
    │   │       ├── alert-dialog.tsx
    │   │       └── input.tsx
    │   ├── constants/
    │   │   └── branding.ts
    │   ├── contexts/
    │   │   ├── AuthContext.tsx
    │   │   └── LanguageContext.tsx
    │   ├── hooks/
    │   │   ├── useCounterAnimation.ts
    │   │   ├── useGitHubStats.ts
    │   │   └── useSystemConfig.ts
    │   ├── i18n/
    │   │   ├── strategy-translations.ts
    │   │   └── translations.ts
    │   ├── index.css
    │   ├── lib/
    │   │   ├── api/
    │   │   │   ├── config.ts
    │   │   │   ├── data.ts
    │   │   │   ├── helpers.ts
    │   │   │   ├── index.ts
    │   │   │   ├── strategies.ts
    │   │   │   ├── telegram.ts
    │   │   │   └── traders.ts
    │   │   ├── apiGuard.test.ts
    │   │   ├── clipboard.ts
    │   │   ├── cn.ts
    │   │   ├── config.ts
    │   │   ├── crypto.ts
    │   │   ├── httpClient.ts
    │   │   ├── notify.tsx
    │   │   ├── registrationToggle.test.ts
    │   │   └── text.ts
    │   ├── main.tsx
    │   ├── pages/
    │   │   ├── DataPage.tsx
    │   │   ├── FAQPage.tsx
    │   │   ├── LandingPage.tsx
    │   │   ├── PageNotFound.tsx
    │   │   ├── SettingsPage.tsx
    │   │   ├── StrategyMarketPage.tsx
    │   │   ├── StrategyStudioPage.tsx
    │   │   └── TraderDashboardPage.tsx
    │   ├── stores/
    │   │   ├── index.ts
    │   │   ├── tradersConfigStore.ts
    │   │   └── tradersModalStore.ts
    │   ├── test/
    │   │   └── setup.ts
    │   ├── types/
    │   │   ├── config.ts
    │   │   ├── index.ts
    │   │   ├── strategy.ts
    │   │   └── trading.ts
    │   ├── utils/
    │   │   ├── format.ts
    │   │   ├── indicators.ts
    │   │   └── traderColors.ts
    │   └── vite-env.d.ts
    ├── tailwind.config.js
    ├── tsconfig.json
    ├── tsconfig.node.json
    ├── vite.config.ts
    └── vitest.config.ts
Download .txt
Showing preview only (266K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2816 symbols across 330 files)

FILE: .github/workflows/scripts/calculate_coverage.py
  function parse_coverage_file (line 15) | def parse_coverage_file(coverage_file: str) -> Tuple[float, Dict[str, fl...
  function get_coverage_status (line 90) | def get_coverage_status(coverage: float) -> Tuple[str, str, str]:
  function generate_coverage_report (line 110) | def generate_coverage_report(coverage_file: str, output_file: str) -> None:
  function set_github_output (line 138) | def set_github_output(name: str, value: str) -> None:
  function main (line 154) | def main():

FILE: .github/workflows/scripts/comment_pr.py
  function read_file (line 16) | def read_file(file_path: str) -> str:
  function generate_comment_body (line 26) | def generate_comment_body(coverage: str, emoji: str, status: str,
  function find_existing_comment (line 71) | def find_existing_comment(token: str, repo: str, pr_number: int) -> Opti...
  function post_comment (line 106) | def post_comment(token: str, repo: str, pr_number: int, body: str) -> bool:
  function update_comment (line 138) | def update_comment(token: str, repo: str, comment_id: int, body: str) ->...
  function is_fork_pr (line 170) | def is_fork_pr(event_path: str) -> bool:
  function main (line 194) | def main():

FILE: api/crypto_handler.go
  type CryptoHandler (line 13) | type CryptoHandler struct
    method HandleGetCryptoConfig (line 27) | func (h *CryptoHandler) HandleGetCryptoConfig(c *gin.Context) {
    method HandleGetPublicKey (line 37) | func (h *CryptoHandler) HandleGetPublicKey(c *gin.Context) {
    method HandleDecryptSensitiveData (line 59) | func (h *CryptoHandler) HandleDecryptSensitiveData(c *gin.Context) {
  function NewCryptoHandler (line 18) | func NewCryptoHandler(cryptoService *crypto.CryptoService) *CryptoHandler {
  function isValidPrivateKey (line 86) | func isValidPrivateKey(key string) bool {

FILE: api/errors.go
  function SafeError (line 13) | func SafeError(c *gin.Context, statusCode int, publicMsg string, interna...
  function SafeInternalError (line 23) | func SafeInternalError(c *gin.Context, operation string, err error) {
  function SafeBadRequest (line 30) | func SafeBadRequest(c *gin.Context, msg string) {
  function SafeNotFound (line 35) | func SafeNotFound(c *gin.Context, resource string) {
  function SafeUnauthorized (line 40) | func SafeUnauthorized(c *gin.Context) {
  function SafeForbidden (line 45) | func SafeForbidden(c *gin.Context, msg string) {
  function IsSensitiveError (line 50) | func IsSensitiveError(err error) bool {
  function SanitizeError (line 87) | func SanitizeError(err error, fallbackMsg string) string {

FILE: api/handler_ai_model.go
  type ModelConfig (line 17) | type ModelConfig struct
  type SafeModelConfig (line 27) | type SafeModelConfig struct
  type UpdateModelConfigRequest (line 36) | type UpdateModelConfigRequest struct
  method handleGetModelConfigs (line 46) | func (s *Server) handleGetModelConfigs(c *gin.Context) {
  method handleUpdateModelConfigs (line 92) | func (s *Server) handleUpdateModelConfigs(c *gin.Context) {
  method handleGetSupportedModels (line 194) | func (s *Server) handleGetSupportedModels(c *gin.Context) {

FILE: api/handler_competition.go
  method handleDecisions (line 17) | func (s *Server) handleDecisions(c *gin.Context) {
  method handleLatestDecisions (line 41) | func (s *Server) handleLatestDecisions(c *gin.Context) {
  method handleStatistics (line 81) | func (s *Server) handleStatistics(c *gin.Context) {
  method handleCompetition (line 104) | func (s *Server) handleCompetition(c *gin.Context) {
  method handleEquityHistory (line 124) | func (s *Server) handleEquityHistory(c *gin.Context) {
  method handlePublicTraderList (line 184) | func (s *Server) handlePublicTraderList(c *gin.Context) {
  method handlePublicCompetition (line 228) | func (s *Server) handlePublicCompetition(c *gin.Context) {
  method handleTopTraders (line 239) | func (s *Server) handleTopTraders(c *gin.Context) {
  method handleEquityHistoryBatch (line 251) | func (s *Server) handleEquityHistoryBatch(c *gin.Context) {
  method getEquityHistoryForTraders (line 321) | func (s *Server) getEquityHistoryForTraders(traderIDs []string, hours in...
  method handleGetPublicTraderConfig (line 441) | func (s *Server) handleGetPublicTraderConfig(c *gin.Context) {

FILE: api/handler_exchange.go
  type ExchangeConfig (line 15) | type ExchangeConfig struct
  type SafeExchangeConfig (line 26) | type SafeExchangeConfig struct
  type UpdateExchangeConfigRequest (line 40) | type UpdateExchangeConfigRequest struct
  type CreateExchangeRequest (line 60) | type CreateExchangeRequest struct
  method handleGetExchangeConfigs (line 80) | func (s *Server) handleGetExchangeConfigs(c *gin.Context) {
  method handleUpdateExchangeConfigs (line 120) | func (s *Server) handleUpdateExchangeConfigs(c *gin.Context) {
  method handleCreateExchange (line 213) | func (s *Server) handleCreateExchange(c *gin.Context) {
  method handleDeleteExchange (line 295) | func (s *Server) handleDeleteExchange(c *gin.Context) {
  method handleGetSupportedExchanges (line 335) | func (s *Server) handleGetSupportedExchanges(c *gin.Context) {

FILE: api/handler_klines.go
  method handleKlines (line 23) | func (s *Server) handleKlines(c *gin.Context) {
  method getKlinesFromCoinank (line 84) | func (s *Server) getKlinesFromCoinank(symbol, interval, exchange string,...
  method getKlinesFromAlpaca (line 209) | func (s *Server) getKlinesFromAlpaca(symbol, interval string, limit int)...
  method getKlinesFromTwelveData (line 242) | func (s *Server) getKlinesFromTwelveData(symbol, interval string, limit ...
  method getKlinesFromHyperliquid (line 284) | func (s *Server) getKlinesFromHyperliquid(symbol, interval string, limit...
  method handleSymbols (line 324) | func (s *Server) handleSymbols(c *gin.Context) {

FILE: api/handler_order.go
  method handleTraderList (line 14) | func (s *Server) handleTraderList(c *gin.Context) {
  method handleGetTraderConfig (line 60) | func (s *Server) handleGetTraderConfig(c *gin.Context) {
  method handleStatus (line 111) | func (s *Server) handleStatus(c *gin.Context) {
  method handleAccount (line 129) | func (s *Server) handleAccount(c *gin.Context) {
  method handlePositions (line 159) | func (s *Server) handlePositions(c *gin.Context) {
  method handlePositionHistory (line 182) | func (s *Server) handlePositionHistory(c *gin.Context) {
  method handleTrades (line 234) | func (s *Server) handleTrades(c *gin.Context) {
  method handleOrders (line 289) | func (s *Server) handleOrders(c *gin.Context) {
  method handleOrderFills (line 334) | func (s *Server) handleOrderFills(c *gin.Context) {
  method handleOpenOrders (line 371) | func (s *Server) handleOpenOrders(c *gin.Context) {

FILE: api/handler_telegram.go
  method handleGetTelegramConfig (line 10) | func (s *Server) handleGetTelegramConfig(c *gin.Context) {
  method handleUpdateTelegramConfig (line 44) | func (s *Server) handleUpdateTelegramConfig(c *gin.Context) {
  method handleUnbindTelegram (line 75) | func (s *Server) handleUnbindTelegram(c *gin.Context) {
  method handleUpdateTelegramModel (line 84) | func (s *Server) handleUpdateTelegramModel(c *gin.Context) {

FILE: api/handler_trader.go
  type CreateTraderRequest (line 26) | type CreateTraderRequest struct
  type UpdateTraderRequest (line 47) | type UpdateTraderRequest struct
  method handleCreateTrader (line 66) | func (s *Server) handleCreateTrader(c *gin.Context) {
  method handleUpdateTrader (line 303) | func (s *Server) handleUpdateTrader(c *gin.Context) {
  method handleDeleteTrader (line 448) | func (s *Server) handleDeleteTrader(c *gin.Context) {
  method handleStartTrader (line 476) | func (s *Server) handleStartTrader(c *gin.Context) {
  method handleStopTrader (line 565) | func (s *Server) handleStopTrader(c *gin.Context) {

FILE: api/handler_trader_config.go
  method handleUpdateTraderPrompt (line 12) | func (s *Server) handleUpdateTraderPrompt(c *gin.Context) {
  method handleToggleCompetition (line 45) | func (s *Server) handleToggleCompetition(c *gin.Context) {

FILE: api/handler_trader_status.go
  method handleGetGridRiskInfo (line 26) | func (s *Server) handleGetGridRiskInfo(c *gin.Context) {
  method handleSyncBalance (line 40) | func (s *Server) handleSyncBalance(c *gin.Context) {
  method handleClosePosition (line 195) | func (s *Server) handleClosePosition(c *gin.Context) {
  method recordClosePositionOrder (line 354) | func (s *Server) recordClosePositionOrder(traderID, exchangeID, exchange...
  method pollAndUpdateOrderStatus (line 461) | func (s *Server) pollAndUpdateOrderStatus(orderRecordID int64, traderID,...
  method pollLighterTradeHistory (line 549) | func (s *Server) pollLighterTradeHistory(orderRecordID int64, traderID, ...
  function getSideFromAction (line 556) | func getSideFromAction(action string) string {

FILE: api/handler_user.go
  method handleLogout (line 17) | func (s *Server) handleLogout(c *gin.Context) {
  method handleRegister (line 47) | func (s *Server) handleRegister(c *gin.Context) {
  method handleLogin (line 119) | func (s *Server) handleLogin(c *gin.Context) {
  method handleChangePassword (line 159) | func (s *Server) handleChangePassword(c *gin.Context) {
  method handleResetPassword (line 181) | func (s *Server) handleResetPassword(c *gin.Context) {
  method initUserDefaultConfigs (line 218) | func (s *Server) initUserDefaultConfigs(userID string) error {

FILE: api/handler_wallet.go
  type walletValidateRequest (line 18) | type walletValidateRequest struct
  type walletValidateResponse (line 22) | type walletValidateResponse struct
  constant baseRPCURL (line 31) | baseRPCURL      = "https://mainnet.base.org"
  constant usdcContractBase (line 32) | usdcContractBase = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
  constant usdcDecimals (line 33) | usdcDecimals     = 6
  method handleWalletValidate (line 36) | func (s *Server) handleWalletValidate(c *gin.Context) {
  function queryUSDCBalance (line 101) | func queryUSDCBalance(address string) string {
  function checkClaw402Health (line 162) | func checkClaw402Health() string {

FILE: api/route_registry.go
  type RouteDoc (line 11) | type RouteDoc struct
  method route (line 22) | func (s *Server) route(g *gin.RouterGroup, method, path, description str...
  method routeWithSchema (line 28) | func (s *Server) routeWithSchema(g *gin.RouterGroup, method, path, descr...
  function GetAPIDocs (line 50) | func GetAPIDocs() string {

FILE: api/server.go
  type Server (line 20) | type Server struct
    method setupRoutes (line 74) | func (s *Server) setupRoutes() {
    method handleHealth (line 350) | func (s *Server) handleHealth(c *gin.Context) {
    method handleGetSystemConfig (line 358) | func (s *Server) handleGetSystemConfig(c *gin.Context) {
    method handleGetServerIP (line 368) | func (s *Server) handleGetServerIP(c *gin.Context) {
    method getTraderFromQuery (line 497) | func (s *Server) getTraderFromQuery(c *gin.Context) (*manager.TraderMa...
    method authMiddleware (line 527) | func (s *Server) authMiddleware() gin.HandlerFunc {
    method Start (line 570) | func (s *Server) Start() error {
    method Shutdown (line 606) | func (s *Server) Shutdown() error {
    method SetTelegramReloadCh (line 616) | func (s *Server) SetTelegramReloadCh(ch chan<- struct{}) {
  function NewServer (line 31) | func NewServer(traderManager *manager.TraderManager, st *store.Store, cr...
  function corsMiddleware (line 58) | func corsMiddleware() gin.HandlerFunc {
  function getPublicIPFromAPI (line 390) | func getPublicIPFromAPI() string {
  function getPublicIPFromInterface (line 430) | func getPublicIPFromInterface() string {
  function isPrivateIP (line 475) | func isPrivateIP(ip net.IP) bool {

FILE: api/server_test.go
  function TestUpdateTraderRequest_SystemPromptTemplate (line 11) | func TestUpdateTraderRequest_SystemPromptTemplate(t *testing.T) {
  function TestGetTraderConfigResponse_SystemPromptTemplate (line 100) | func TestGetTraderConfigResponse_SystemPromptTemplate(t *testing.T) {
  function TestUpdateTraderRequest_CompleteFields (line 193) | func TestUpdateTraderRequest_CompleteFields(t *testing.T) {
  function TestTraderListResponse_SystemPromptTemplate (line 230) | func TestTraderListResponse_SystemPromptTemplate(t *testing.T) {
  function TestPublicTraderListResponse_SystemPromptTemplate (line 266) | func TestPublicTraderListResponse_SystemPromptTemplate(t *testing.T) {

FILE: api/strategy.go
  function validateStrategyConfig (line 21) | func validateStrategyConfig(config *store.StrategyConfig) []string {
  method handlePublicStrategies (line 35) | func (s *Server) handlePublicStrategies(c *gin.Context) {
  method handleGetStrategies (line 72) | func (s *Server) handleGetStrategies(c *gin.Context) {
  method handleGetStrategy (line 111) | func (s *Server) handleGetStrategy(c *gin.Context) {
  method handleCreateStrategy (line 143) | func (s *Server) handleCreateStrategy(c *gin.Context) {
  method handleUpdateStrategy (line 212) | func (s *Server) handleUpdateStrategy(c *gin.Context) {
  method handleDeleteStrategy (line 304) | func (s *Server) handleDeleteStrategy(c *gin.Context) {
  method handleActivateStrategy (line 322) | func (s *Server) handleActivateStrategy(c *gin.Context) {
  method handleDuplicateStrategy (line 340) | func (s *Server) handleDuplicateStrategy(c *gin.Context) {
  method handleGetActiveStrategy (line 371) | func (s *Server) handleGetActiveStrategy(c *gin.Context) {
  method handleGetDefaultStrategyConfig (line 401) | func (s *Server) handleGetDefaultStrategyConfig(c *gin.Context) {
  method handlePreviewPrompt (line 414) | func (s *Server) handlePreviewPrompt(c *gin.Context) {
  method handleStrategyTestRun (line 463) | func (s *Server) handleStrategyTestRun(c *gin.Context) {
  method runRealAITest (line 627) | func (s *Server) runRealAITest(userID, modelID, systemPrompt, userPrompt...

FILE: api/traderid_test.go
  function TestTraderIDUniqueness (line 13) | func TestTraderIDUniqueness(t *testing.T) {
  function generateTraderID (line 44) | func generateTraderID(exchangeID, aiModelID string) string {
  function isValidTraderIDFormat (line 49) | func isValidTraderIDFormat(traderID, expectedExchange, expectedModel str...
  function TestTraderIDFormat (line 72) | func TestTraderIDFormat(t *testing.T) {
  function TestTraderIDNoCollision (line 107) | func TestTraderIDNoCollision(t *testing.T) {

FILE: api/utils.go
  function MaskSensitiveString (line 7) | func MaskSensitiveString(s string) string {
  function SanitizeModelConfigForLog (line 19) | func SanitizeModelConfigForLog(models map[string]struct {
  function SanitizeExchangeConfigForLog (line 38) | func SanitizeExchangeConfigForLog(exchanges map[string]struct {
  function MaskEmail (line 91) | func MaskEmail(email string) string {

FILE: api/utils_test.go
  function TestMaskSensitiveString (line 7) | func TestMaskSensitiveString(t *testing.T) {
  function TestSanitizeModelConfigForLog (line 50) | func TestSanitizeModelConfigForLog(t *testing.T) {
  function TestSanitizeExchangeConfigForLog (line 90) | func TestSanitizeExchangeConfigForLog(t *testing.T) {
  function TestMaskEmail (line 163) | func TestMaskEmail(t *testing.T) {

FILE: auth/auth.go
  constant maxBlacklistEntries (line 23) | maxBlacklistEntries = 100_000
  function SetJWTSecret (line 26) | func SetJWTSecret(secret string) {
  function BlacklistToken (line 31) | func BlacklistToken(token string, exp time.Time) {
  function IsTokenBlacklisted (line 52) | func IsTokenBlacklisted(token string) bool {
  type Claims (line 66) | type Claims struct
  function HashPassword (line 73) | func HashPassword(password string) (string, error) {
  function CheckPassword (line 79) | func CheckPassword(password, hash string) bool {
  function GenerateJWT (line 85) | func GenerateJWT(userID, email string) (string, error) {
  function ValidateJWT (line 102) | func ValidateJWT(tokenString string) (*Claims, error) {

FILE: config/config.go
  type Config (line 16) | type Config struct
  function Init (line 49) | func Init() {
  function Get (line 140) | func Get() *Config {

FILE: crypto/crypto.go
  constant storagePrefix (line 23) | storagePrefix    = "ENC:v1:"
  constant storageDelimiter (line 24) | storageDelimiter = ":"
  constant EnvDataEncryptionKey (line 29) | EnvDataEncryptionKey = "DATA_ENCRYPTION_KEY"
  constant EnvRSAPrivateKey (line 30) | EnvRSAPrivateKey     = "RSA_PRIVATE_KEY"
  type EncryptedPayload (line 33) | type EncryptedPayload struct
  type AADData (line 42) | type AADData struct
  type CryptoService (line 49) | type CryptoService struct
    method HasDataKey (line 167) | func (cs *CryptoService) HasDataKey() bool {
    method GetPublicKeyPEM (line 171) | func (cs *CryptoService) GetPublicKeyPEM() string {
    method EncryptForStorage (line 185) | func (cs *CryptoService) EncryptForStorage(plaintext string, aadParts ...
    method DecryptFromStorage (line 219) | func (cs *CryptoService) DecryptFromStorage(value string, aadParts ......
    method IsEncryptedStorageValue (line 269) | func (cs *CryptoService) IsEncryptedStorageValue(value string) bool {
    method DecryptPayload (line 284) | func (cs *CryptoService) DecryptPayload(payload *EncryptedPayload) ([]...
    method DecryptSensitiveData (line 351) | func (cs *CryptoService) DecryptSensitiveData(payload *EncryptedPayloa...
  function NewCryptoService (line 56) | func NewCryptoService() (*CryptoService, error) {
  function loadRSAPrivateKeyFromEnv (line 77) | func loadRSAPrivateKeyFromEnv() (*rsa.PrivateKey, error) {
  function loadDataKeyFromEnv (line 90) | func loadDataKeyFromEnv() ([]byte, error) {
  function ParseRSAPrivateKeyFromPEM (line 109) | func ParseRSAPrivateKeyFromPEM(pemBytes []byte) (*rsa.PrivateKey, error) {
  function decodePossibleKey (line 134) | func decodePossibleKey(value string) ([]byte, bool) {
  function normalizeAESKey (line 153) | func normalizeAESKey(raw []byte) ([]byte, bool) {
  function composeAAD (line 273) | func composeAAD(parts []string) []byte {
  function isEncryptedStorageValue (line 280) | func isEncryptedStorageValue(value string) bool {
  function GenerateKeyPair (line 361) | func GenerateKeyPair() (privateKeyPEM, publicKeyPEM string, err error) {
  function GenerateDataKey (line 389) | func GenerateDataKey() (string, error) {
  function SetGlobalCryptoService (line 405) | func SetGlobalCryptoService(cs *CryptoService) {
  type EncryptedString (line 411) | type EncryptedString
    method Scan (line 415) | func (es *EncryptedString) Scan(value interface{}) error {
    method Value (line 449) | func (es EncryptedString) Value() (driver.Value, error) {
    method String (line 467) | func (es EncryptedString) String() string {

FILE: hook/hooks.go
  type HookFunc (line 7) | type HookFunc
  function HookExec (line 14) | func HookExec[T any](key string, args ...any) *T {
  function RegisterHook (line 30) | func RegisterHook(key string, hook HookFunc) {
  constant GETIP (line 36) | GETIP              = "GETIP"
  constant NEW_BINANCE_TRADER (line 37) | NEW_BINANCE_TRADER = "NEW_BINANCE_TRADER"
  constant NEW_ASTER_TRADER (line 38) | NEW_ASTER_TRADER   = "NEW_ASTER_TRADER"
  constant SET_HTTP_CLIENT (line 39) | SET_HTTP_CLIENT    = "SET_HTTP_CLIENT"

FILE: hook/http_client_hook.go
  type SetHttpClientResult (line 8) | type SetHttpClientResult struct
    method Error (line 13) | func (r *SetHttpClientResult) Error() error {
    method GetResult (line 20) | func (r *SetHttpClientResult) GetResult() *http.Client {

FILE: hook/ip_hook.go
  type IpResult (line 5) | type IpResult struct
    method Error (line 10) | func (r *IpResult) Error() error {
    method GetResult (line 14) | func (r *IpResult) GetResult() string {

FILE: hook/trader_hook.go
  type NewBinanceTraderResult (line 10) | type NewBinanceTraderResult struct
    method Error (line 15) | func (r *NewBinanceTraderResult) Error() error {
    method GetResult (line 22) | func (r *NewBinanceTraderResult) GetResult() *futures.Client {
  type NewAsterTraderResult (line 27) | type NewAsterTraderResult struct
    method Error (line 32) | func (r *NewAsterTraderResult) Error() error {
    method GetResult (line 39) | func (r *NewAsterTraderResult) GetResult() *http.Client {

FILE: kernel/engine.go
  type PositionInfo (line 24) | type PositionInfo struct
  type AccountInfo (line 40) | type AccountInfo struct
  type CandidateCoin (line 52) | type CandidateCoin struct
  type OITopData (line 58) | type OITopData struct
  type TradingStats (line 66) | type TradingStats struct
  type RecentOrder (line 78) | type RecentOrder struct
  type Context (line 91) | type Context struct
  type Decision (line 114) | type Decision struct
  type FullDecision (line 138) | type FullDecision struct
  type QuantData (line 149) | type QuantData struct
  type NetflowData (line 157) | type NetflowData struct
  type FlowTypeData (line 162) | type FlowTypeData struct
  type OIData (line 167) | type OIData struct
  type OIDeltaData (line 172) | type OIDeltaData struct
  type StrategyEngine (line 183) | type StrategyEngine struct
    method GetRiskControlConfig (line 204) | func (e *StrategyEngine) GetRiskControlConfig() store.RiskControlConfig {
    method GetLanguage (line 209) | func (e *StrategyEngine) GetLanguage() Language {
    method GetConfig (line 222) | func (e *StrategyEngine) GetConfig() *store.StrategyConfig {
    method GetCandidateCoins (line 231) | func (e *StrategyEngine) GetCandidateCoins() ([]CandidateCoin, error) {
    method filterExcludedCoins (line 426) | func (e *StrategyEngine) filterExcludedCoins(candidates []CandidateCoi...
    method getAI500Coins (line 451) | func (e *StrategyEngine) getAI500Coins(limit int) ([]CandidateCoin, er...
    method getOITopCoins (line 471) | func (e *StrategyEngine) getOITopCoins(limit int) ([]CandidateCoin, er...
    method getOILowCoins (line 495) | func (e *StrategyEngine) getOILowCoins(limit int) ([]CandidateCoin, er...
    method getHyperAllCoins (line 520) | func (e *StrategyEngine) getHyperAllCoins() ([]CandidateCoin, error) {
    method getHyperMainCoins (line 541) | func (e *StrategyEngine) getHyperMainCoins(limit int) ([]CandidateCoin...
    method FetchMarketData (line 570) | func (e *StrategyEngine) FetchMarketData(symbol string) (*market.Data,...
    method FetchExternalData (line 575) | func (e *StrategyEngine) FetchExternalData() (map[string]interface{}, ...
    method fetchSingleExternalSource (line 590) | func (e *StrategyEngine) fetchSingleExternalSource(source store.Extern...
    method FetchQuantData (line 652) | func (e *StrategyEngine) FetchQuantData(symbol string) (*QuantData, er...
    method FetchQuantDataBatch (line 725) | func (e *StrategyEngine) FetchQuantDataBatch(symbols []string) map[str...
    method FetchOIRankingData (line 747) | func (e *StrategyEngine) FetchOIRankingData() *nofxos.OIRankingData {
    method FetchNetFlowRankingData (line 778) | func (e *StrategyEngine) FetchNetFlowRankingData() *nofxos.NetFlowRank...
    method FetchPriceRankingData (line 810) | func (e *StrategyEngine) FetchPriceRankingData() *nofxos.PriceRankingD...
  function NewStrategyEngine (line 189) | func NewStrategyEngine(config *store.StrategyConfig) *StrategyEngine {
  function extractJSONPath (line 636) | func extractJSONPath(data interface{}, path string) interface{} {
  function detectLanguage (line 845) | func detectLanguage(text string) Language {

FILE: kernel/engine_analysis.go
  function GetFullDecision (line 38) | func GetFullDecision(ctx *Context, mcpClient mcp.AIClient) (*FullDecisio...
  function GetFullDecisionWithStrategy (line 45) | func GetFullDecisionWithStrategy(ctx *Context, mcpClient mcp.AIClient, e...
  function fetchMarketDataWithStrategy (line 122) | func fetchMarketDataWithStrategy(ctx *Context, engine *StrategyEngine) e...
  function parseFullDecisionResponse (line 203) | func parseFullDecisionResponse(aiResponse string, accountEquity float64,...
  function extractCoTTrace (line 227) | func extractCoTTrace(response string) string {
  function extractDecisions (line 247) | func extractDecisions(response string) ([]Decision, error) {
  function fixMissingQuotes (line 310) | func fixMissingQuotes(jsonStr string) string {
  function validateJSONFormat (line 334) | func validateJSONFormat(jsonStr string) error {
  function min (line 361) | func min(a, b int) int {
  function removeInvisibleRunes (line 368) | func removeInvisibleRunes(s string) string {
  function compactArrayOpen (line 372) | func compactArrayOpen(s string) string {

FILE: kernel/engine_position.go
  function validateDecisions (line 12) | func validateDecisions(decisions []Decision, accountEquity float64, btcE...
  function validateDecision (line 21) | func validateDecision(d *Decision, accountEquity float64, btcEthLeverage...

FILE: kernel/engine_prompt.go
  method BuildSystemPrompt (line 17) | func (e *StrategyEngine) BuildSystemPrompt(accountEquity float64, varian...
  method writeAvailableIndicators (line 155) | func (e *StrategyEngine) writeAvailableIndicators(sb *strings.Builder) {
  method BuildUserPrompt (line 228) | func (e *StrategyEngine) BuildUserPrompt(ctx *Context) string {
  method formatPositionInfo (line 402) | func (e *StrategyEngine) formatPositionInfo(index int, pos PositionInfo,...
  method formatCoinSourceTag (line 442) | func (e *StrategyEngine) formatCoinSourceTag(sources []string) string {
  method formatMarketData (line 503) | func (e *StrategyEngine) formatMarketData(data *market.Data) string {
  method formatTimeframeSeriesData (line 613) | func (e *StrategyEngine) formatTimeframeSeriesData(sb *strings.Builder, ...
  method formatQuantData (line 669) | func (e *StrategyEngine) formatQuantData(data *QuantData) string {
  function formatFlowValue (line 754) | func formatFlowValue(v float64) string {
  function formatFloatSlice (line 773) | func formatFloatSlice(values []float64) string {

FILE: kernel/formatter.go
  function FormatContextForAI (line 20) | func FormatContextForAI(ctx *Context, lang Language) string {
  function FormatContextDataOnly (line 34) | func FormatContextDataOnly(ctx *Context, lang Language) string {
  function formatContextData (line 39) | func formatContextData(ctx *Context, lang Language) string {
  function formatHeaderZH (line 107) | func formatHeaderZH(ctx *Context) string {
  function formatAccountZH (line 113) | func formatAccountZH(ctx *Context) string {
  function formatTradingStatsZH (line 135) | func formatTradingStatsZH(stats *TradingStats) string {
  function formatRecentTradesZH (line 196) | func formatRecentTradesZH(orders []RecentOrder) string {
  function formatCurrentPositionsZH (line 227) | func formatCurrentPositionsZH(ctx *Context) string {
  function formatCandidateCoinsZH (line 270) | func formatCandidateCoinsZH(ctx *Context) string {
  function formatKlineDataZH (line 319) | func formatKlineDataZH(symbol string, tfData map[string]*market.Timefram...
  function getOIInterpretationZH (line 361) | func getOIInterpretationZH(oiChange, priceChange string) string {
  function formatHeaderEN (line 376) | func formatHeaderEN(ctx *Context) string {
  function formatAccountEN (line 382) | func formatAccountEN(ctx *Context) string {
  function formatTradingStatsEN (line 404) | func formatTradingStatsEN(stats *TradingStats) string {
  function formatRecentTradesEN (line 465) | func formatRecentTradesEN(orders []RecentOrder) string {
  function formatCurrentPositionsEN (line 495) | func formatCurrentPositionsEN(ctx *Context) string {
  function formatCandidateCoinsEN (line 536) | func formatCandidateCoinsEN(ctx *Context) string {
  function formatKlineDataEN (line 581) | func formatKlineDataEN(symbol string, tfData map[string]*market.Timefram...
  function getOIInterpretationEN (line 626) | func getOIInterpretationEN(oiChange, priceChange string) string {

FILE: kernel/grid_engine.go
  type GridLevelInfo (line 19) | type GridLevelInfo struct
  type GridContext (line 33) | type GridContext struct
  function BuildGridSystemPrompt (line 97) | func BuildGridSystemPrompt(config *store.GridStrategyConfig, lang string...
  function buildGridSystemPromptZh (line 104) | func buildGridSystemPromptZh(config *store.GridStrategyConfig) string {
  function buildGridSystemPromptEn (line 156) | func buildGridSystemPromptEn(config *store.GridStrategyConfig) string {
  function BuildGridUserPrompt (line 209) | func BuildGridUserPrompt(ctx *GridContext, lang string) string {
  function buildGridUserPromptZh (line 216) | func buildGridUserPromptZh(ctx *GridContext) string {
  function buildGridUserPromptEn (line 327) | func buildGridUserPromptEn(ctx *GridContext) string {
  function GetGridDecisions (line 443) | func GetGridDecisions(ctx *GridContext, mcpClient mcp.AIClient, config *...
  function parseGridDecisions (line 489) | func parseGridDecisions(response string, symbol string) ([]Decision, err...
  function extractJSONArray (line 516) | func extractJSONArray(response string) string {
  function isValidGridAction (line 533) | func isValidGridAction(action string) bool {
  function BuildGridContextFromMarketData (line 557) | func BuildGridContextFromMarketData(mktData *market.Data, config *store....
  function max (line 613) | func max(a, b int) int {

FILE: kernel/prompt_builder.go
  type PromptBuilder (line 15) | type PromptBuilder struct
    method BuildSystemPrompt (line 25) | func (pb *PromptBuilder) BuildSystemPrompt() string {
    method BuildUserPrompt (line 33) | func (pb *PromptBuilder) BuildUserPrompt(ctx *Context) string {
    method buildSystemPromptZH (line 46) | func (pb *PromptBuilder) buildSystemPromptZH() string {
    method getDecisionRequirementsZH (line 125) | func (pb *PromptBuilder) getDecisionRequirementsZH() string {
    method buildSystemPromptEN (line 181) | func (pb *PromptBuilder) buildSystemPromptEN() string {
    method getDecisionRequirementsEN (line 260) | func (pb *PromptBuilder) getDecisionRequirementsEN() string {
  function NewPromptBuilder (line 20) | func NewPromptBuilder(lang Language) *PromptBuilder {
  function FormatDecisionExample (line 317) | func FormatDecisionExample(lang Language) string {
  function ValidateDecisionFormat (line 334) | func ValidateDecisionFormat(decisions []Decision) error {

FILE: kernel/prompt_builder_test.go
  function TestPromptBuilder (line 10) | func TestPromptBuilder(t *testing.T) {
  function TestValidateDecisionFormat (line 144) | func TestValidateDecisionFormat(t *testing.T) {
  function TestFormatDecisionExample (line 337) | func TestFormatDecisionExample(t *testing.T) {
  function BenchmarkBuildSystemPrompt (line 370) | func BenchmarkBuildSystemPrompt(b *testing.B) {
  function BenchmarkBuildUserPrompt (line 388) | func BenchmarkBuildUserPrompt(b *testing.B) {
  function createTestContext (line 407) | func createTestContext() *Context {

FILE: kernel/schema.go
  constant SchemaVersion (line 11) | SchemaVersion = "1.0.0"
  type Language (line 15) | type Language
  constant LangChinese (line 18) | LangChinese Language = "zh-CN"
  constant LangEnglish (line 19) | LangEnglish Language = "en-US"
  type BilingualFieldDef (line 25) | type BilingualFieldDef struct
    method GetName (line 36) | func (d BilingualFieldDef) GetName(lang Language) string {
    method GetFormula (line 44) | func (d BilingualFieldDef) GetFormula(lang Language) string {
    method GetDesc (line 52) | func (d BilingualFieldDef) GetDesc(lang Language) string {
  type BilingualRuleDef (line 223) | type BilingualRuleDef struct
    method GetDesc (line 232) | func (d BilingualRuleDef) GetDesc(lang Language) string {
    method GetReason (line 240) | func (d BilingualRuleDef) GetReason(lang Language) string {
  type OIInterpretationType (line 346) | type OIInterpretationType struct
  type CommonMistake (line 399) | type CommonMistake struct
  function GetSchemaPrompt (line 446) | func GetSchemaPrompt(lang Language) string {
  function getSchemaPromptZH (line 454) | func getSchemaPromptZH() string {
  function getSchemaPromptEN (line 493) | func getSchemaPromptEN() string {
  function formatFieldDefZH (line 532) | func formatFieldDefZH(key string, field BilingualFieldDef) string {
  function formatFieldDefEN (line 545) | func formatFieldDefEN(key string, field BilingualFieldDef) string {

FILE: kernel/validate_test.go
  function TestLeverageFallback (line 8) | func TestLeverageFallback(t *testing.T) {
  function contains (line 105) | func contains(s, substr string) bool {
  function stringContains (line 110) | func stringContains(s, substr string) bool {

FILE: logger/config.go
  type Config (line 4) | type Config struct
    method SetDefaults (line 9) | func (c *Config) SetDefaults() {

FILE: logger/logger.go
  type compactFormatter (line 23) | type compactFormatter struct
    method Format (line 27) | func (f *compactFormatter) Format(entry *logrus.Entry) ([]byte, error) {
  function init (line 52) | func init() {
  function Init (line 66) | func Init(cfg *Config) error {
  function InitWithSimpleConfig (line 110) | func InitWithSimpleConfig(level string) error {
  function Shutdown (line 115) | func Shutdown() {
  function WithFields (line 127) | func WithFields(fields logrus.Fields) *logrus.Entry {
  function WithField (line 132) | func WithField(key string, value interface{}) *logrus.Entry {
  function Debug (line 137) | func Debug(args ...interface{}) {
  function Info (line 141) | func Info(args ...interface{}) {
  function Warn (line 145) | func Warn(args ...interface{}) {
  function Debugf (line 149) | func Debugf(format string, args ...interface{}) {
  function Infof (line 153) | func Infof(format string, args ...interface{}) {
  function Warnf (line 157) | func Warnf(format string, args ...interface{}) {
  function Error (line 161) | func Error(args ...interface{}) {
  function Errorf (line 165) | func Errorf(format string, args ...interface{}) {
  function Fatal (line 169) | func Fatal(args ...interface{}) {
  function Fatalf (line 173) | func Fatalf(format string, args ...interface{}) {
  function Panic (line 177) | func Panic(args ...interface{}) {
  function Panicf (line 181) | func Panicf(format string, args ...interface{}) {
  type MCPLogger (line 191) | type MCPLogger struct
    method Debugf (line 198) | func (l *MCPLogger) Debugf(format string, args ...any) {
    method Infof (line 202) | func (l *MCPLogger) Infof(format string, args ...any) {
    method Warnf (line 206) | func (l *MCPLogger) Warnf(format string, args ...any) {
    method Errorf (line 210) | func (l *MCPLogger) Errorf(format string, args ...any) {
  function NewMCPLogger (line 194) | func NewMCPLogger() *MCPLogger {

FILE: main.go
  function main (line 24) | func main() {
  function initInstallationID (line 160) | func initInstallationID(st *store.Store) {

FILE: manager/trader_manager.go
  type CompetitionCache (line 15) | type CompetitionCache struct
  type TraderManager (line 22) | type TraderManager struct
    method GetLoadError (line 41) | func (tm *TraderManager) GetLoadError(traderID string) error {
    method GetTrader (line 48) | func (tm *TraderManager) GetTrader(id string) (*trader.AutoTrader, err...
    method GetAllTraders (line 60) | func (tm *TraderManager) GetAllTraders() map[string]*trader.AutoTrader {
    method GetTraderIDs (line 72) | func (tm *TraderManager) GetTraderIDs() []string {
    method StartAll (line 84) | func (tm *TraderManager) StartAll() {
    method StopAll (line 100) | func (tm *TraderManager) StopAll() {
    method AutoStartRunningTraders (line 111) | func (tm *TraderManager) AutoStartRunningTraders(st *store.Store) {
    method GetComparisonData (line 154) | func (tm *TraderManager) GetComparisonData() (map[string]interface{}, ...
    method GetCompetitionData (line 191) | func (tm *TraderManager) GetCompetitionData() (map[string]interface{},...
    method getConcurrentTraderData (line 260) | func (tm *TraderManager) getConcurrentTraderData(traders []*trader.Aut...
    method GetTopTradersData (line 359) | func (tm *TraderManager) GetTopTradersData() (map[string]interface{}, ...
    method RemoveTrader (line 390) | func (tm *TraderManager) RemoveTrader(traderID string) {
    method LoadUserTradersFromStore (line 407) | func (tm *TraderManager) LoadUserTradersFromStore(st *store.Store, use...
    method LoadTradersFromStore (line 503) | func (tm *TraderManager) LoadTradersFromStore(st *store.Store) error {
    method addTraderFromStore (line 605) | func (tm *TraderManager) addTraderFromStore(traderCfg *store.Trader, a...
  function NewTraderManager (line 30) | func NewTraderManager() *TraderManager {

FILE: market/api_client.go
  constant baseURL (line 15) | baseURL = "https://fapi.binance.com"
  type APIClient (line 18) | type APIClient struct
    method GetExchangeInfo (line 38) | func (c *APIClient) GetExchangeInfo() (*ExchangeInfo, error) {
    method GetKlines (line 59) | func (c *APIClient) GetKlines(symbol, interval string, limit int) ([]K...
    method GetCurrentPrice (line 126) | func (c *APIClient) GetCurrentPrice(symbol string) (float64, error) {
  function NewAPIClient (line 22) | func NewAPIClient() *APIClient {
  function parseKline (line 103) | func parseKline(kr KlineResponse) (Kline, error) {

FILE: market/data.go
  type FundingRateCache (line 17) | type FundingRateCache struct
  function Get (line 28) | func Get(symbol string) (*Data, error) {
  function GetWithExchange (line 33) | func GetWithExchange(symbol, exchange string) (*Data, error) {
  function GetWithTimeframes (line 147) | func GetWithTimeframes(symbol string, timeframes []string, primaryTimefr...
  function getOpenInterestData (line 259) | func getOpenInterestData(symbol string) (*OIData, error) {
  function getFundingRate (line 293) | func getFundingRate(symbol string) (float64, error) {
  function Format (line 345) | func Format(data *Data) string {
  function formatTimeframeData (line 433) | func formatTimeframeData(sb *strings.Builder, data *TimeframeSeriesData) {
  function formatPriceWithDynamicPrecision (line 486) | func formatPriceWithDynamicPrecision(price float64) string {
  function formatFloatSlice (line 516) | func formatFloatSlice(values []float64) string {
  function IsXyzDexAsset (line 541) | func IsXyzDexAsset(symbol string) bool {
  function Normalize (line 557) | func Normalize(symbol string) string {
  function parseFloat (line 590) | func parseFloat(v interface{}) (float64, error) {
  function BuildDataFromKlines (line 606) | func BuildDataFromKlines(symbol string, primary []Kline, longer []Kline)...
  function priceChangeFromSeries (line 636) | func priceChangeFromSeries(series []Kline, duration time.Duration) float...
  function isStaleData (line 656) | func isStaleData(klines []Kline, symbol string) bool {

FILE: market/data_indicators.go
  function calculateEMA (line 6) | func calculateEMA(klines []Kline, period int) float64 {
  function calculateMACD (line 28) | func calculateMACD(klines []Kline) float64 {
  function calculateRSI (line 42) | func calculateRSI(klines []Kline, period int) float64 {
  function calculateATR (line 86) | func calculateATR(klines []Kline, period int) float64 {
  function calculateBOLL (line 121) | func calculateBOLL(klines []Kline, period int, multiplier float64) (uppe...
  function calculateDonchian (line 150) | func calculateDonchian(klines []Kline, period int) (upper, lower float64) {
  constant ShortBoxPeriod (line 178) | ShortBoxPeriod = 72
  constant MidBoxPeriod (line 179) | MidBoxPeriod   = 240
  constant LongBoxPeriod (line 180) | LongBoxPeriod  = 500
  function calculateBoxData (line 184) | func calculateBoxData(klines []Kline, currentPrice float64) *BoxData {
  function ExportCalculateEMA (line 203) | func ExportCalculateEMA(klines []Kline, period int) float64 {
  function ExportCalculateMACD (line 208) | func ExportCalculateMACD(klines []Kline) float64 {
  function ExportCalculateRSI (line 213) | func ExportCalculateRSI(klines []Kline, period int) float64 {
  function ExportCalculateATR (line 218) | func ExportCalculateATR(klines []Kline, period int) float64 {
  function ExportCalculateBOLL (line 223) | func ExportCalculateBOLL(klines []Kline, period int, multiplier float64)...
  function ExportCalculateDonchian (line 228) | func ExportCalculateDonchian(klines []Kline, period int) (float64, float...
  function ExportCalculateBoxData (line 233) | func ExportCalculateBoxData(klines []Kline, currentPrice float64) *BoxDa...

FILE: market/data_klines.go
  function getKlinesFromCoinAnk (line 18) | func getKlinesFromCoinAnk(symbol, interval, exchange string, limit int) ...
  function getKlinesFromHyperliquid (line 112) | func getKlinesFromHyperliquid(symbol, interval string, limit int) ([]Kli...
  function calculateTimeframeSeries (line 153) | func calculateTimeframeSeries(klines []Kline, timeframe string, count in...
  function calculatePriceChangeByBars (line 238) | func calculatePriceChangeByBars(klines []Kline, timeframe string, target...
  function parseTimeframeToMinutes (line 269) | func parseTimeframeToMinutes(tf string) int {
  function calculateIntradaySeries (line 305) | func calculateIntradaySeries(klines []Kline) *IntradayData {
  function calculateLongerTermData (line 355) | func calculateLongerTermData(klines []Kline) *LongerTermData {
  function GetBoxData (line 401) | func GetBoxData(symbol string) (*BoxData, error) {

FILE: market/data_test.go
  function generateTestKlines (line 9) | func generateTestKlines(count int) []Kline {
  function TestCalculateIntradaySeries_VolumeCollection (line 35) | func TestCalculateIntradaySeries_VolumeCollection(t *testing.T) {
  function TestCalculateIntradaySeries_VolumeValues (line 102) | func TestCalculateIntradaySeries_VolumeValues(t *testing.T) {
  function TestCalculateIntradaySeries_ATR14 (line 132) | func TestCalculateIntradaySeries_ATR14(t *testing.T) {
  function TestCalculateATR (line 187) | func TestCalculateATR(t *testing.T) {
  function TestCalculateATR_TrueRange (line 253) | func TestCalculateATR_TrueRange(t *testing.T) {
  function TestCalculateIntradaySeries_ConsistencyWithOtherIndicators (line 282) | func TestCalculateIntradaySeries_ConsistencyWithOtherIndicators(t *testi...
  function TestCalculateIntradaySeries_EmptyKlines (line 309) | func TestCalculateIntradaySeries_EmptyKlines(t *testing.T) {
  function TestCalculateIntradaySeries_VolumePrecision (line 332) | func TestCalculateIntradaySeries_VolumePrecision(t *testing.T) {
  function TestIsStaleData_NormalData (line 352) | func TestIsStaleData_NormalData(t *testing.T) {
  function TestIsStaleData_PriceFreezeWithZeroVolume (line 369) | func TestIsStaleData_PriceFreezeWithZeroVolume(t *testing.T) {
  function TestIsStaleData_PriceFreezeWithVolume (line 386) | func TestIsStaleData_PriceFreezeWithVolume(t *testing.T) {
  function TestIsStaleData_InsufficientData (line 403) | func TestIsStaleData_InsufficientData(t *testing.T) {
  function TestIsStaleData_ExactlyFiveKlines (line 418) | func TestIsStaleData_ExactlyFiveKlines(t *testing.T) {
  function TestIsStaleData_WithinTolerance (line 449) | func TestIsStaleData_WithinTolerance(t *testing.T) {
  function TestIsStaleData_MixedScenario (line 472) | func TestIsStaleData_MixedScenario(t *testing.T) {
  function TestIsStaleData_EmptyKlines (line 494) | func TestIsStaleData_EmptyKlines(t *testing.T) {
  function TestCalculateDonchian (line 504) | func TestCalculateDonchian(t *testing.T) {
  function TestCalculateDonchian_PartialPeriod (line 524) | func TestCalculateDonchian_PartialPeriod(t *testing.T) {
  function TestCalculateDonchian_InvalidPeriod (line 541) | func TestCalculateDonchian_InvalidPeriod(t *testing.T) {
  function TestCalculateBoxData (line 559) | func TestCalculateBoxData(t *testing.T) {

FILE: market/historical.go
  constant binanceFuturesKlinesURL (line 12) | binanceFuturesKlinesURL = "https://fapi.binance.com/fapi/v1/klines"
  constant binanceMaxKlineLimit (line 13) | binanceMaxKlineLimit    = 1500
  function GetKlinesRange (line 17) | func GetKlinesRange(symbol string, timeframe string, start, end time.Tim...

FILE: market/timeframe.go
  function NormalizeTimeframe (line 26) | func NormalizeTimeframe(tf string) (string, error) {
  function TFDuration (line 38) | func TFDuration(tf string) (time.Duration, error) {
  function MustNormalizeTimeframe (line 47) | func MustNormalizeTimeframe(tf string) string {
  function SupportedTimeframes (line 56) | func SupportedTimeframes() []string {

FILE: market/types.go
  type Data (line 6) | type Data struct
  type KlineBar (line 23) | type KlineBar struct
  type TimeframeSeriesData (line 33) | type TimeframeSeriesData struct
  type OIData (line 51) | type OIData struct
  type IntradayData (line 57) | type IntradayData struct
  type LongerTermData (line 68) | type LongerTermData struct
  type ExchangeInfo (line 80) | type ExchangeInfo struct
  type SymbolInfo (line 84) | type SymbolInfo struct
  type Kline (line 94) | type Kline struct
  type KlineResponse (line 108) | type KlineResponse
  type PriceTicker (line 110) | type PriceTicker struct
  type Ticker24hr (line 115) | type Ticker24hr struct
  type SymbolFeatures (line 124) | type SymbolFeatures struct
  type Alert (line 145) | type Alert struct
  type Config (line 154) | type Config struct
  type AlertThresholds (line 160) | type AlertThresholds struct
  type CleanupConfig (line 167) | type CleanupConfig struct
  type BoxData (line 192) | type BoxData struct
  type RegimeLevel (line 210) | type RegimeLevel
  constant RegimeLevelNarrow (line 213) | RegimeLevelNarrow   RegimeLevel = "narrow"
  constant RegimeLevelStandard (line 214) | RegimeLevelStandard RegimeLevel = "standard"
  constant RegimeLevelWide (line 215) | RegimeLevelWide     RegimeLevel = "wide"
  constant RegimeLevelVolatile (line 216) | RegimeLevelVolatile RegimeLevel = "volatile"
  constant RegimeLevelTrending (line 217) | RegimeLevelTrending RegimeLevel = "trending"
  type BreakoutLevel (line 221) | type BreakoutLevel
  constant BreakoutNone (line 224) | BreakoutNone  BreakoutLevel = "none"
  constant BreakoutShort (line 225) | BreakoutShort BreakoutLevel = "short"
  constant BreakoutMid (line 226) | BreakoutMid   BreakoutLevel = "mid"
  constant BreakoutLong (line 227) | BreakoutLong  BreakoutLevel = "long"
  type GridDirection (line 231) | type GridDirection
    method GetBuySellRatio (line 243) | func (d GridDirection) GetBuySellRatio(biasRatio float64) (buyRatio, s...
  constant GridDirectionNeutral (line 234) | GridDirectionNeutral   GridDirection = "neutral"
  constant GridDirectionLong (line 235) | GridDirectionLong      GridDirection = "long"
  constant GridDirectionShort (line 236) | GridDirectionShort     GridDirection = "short"
  constant GridDirectionLongBias (line 237) | GridDirectionLongBias  GridDirection = "long_bias"
  constant GridDirectionShortBias (line 238) | GridDirectionShortBias GridDirection = "short_bias"

FILE: mcp/client.go
  constant ProviderCustom (line 16) | ProviderCustom = "custom"
  constant MCPClientTemperature (line 18) | MCPClientTemperature = 0.5
  type TokenUsage (line 46) | type TokenUsage struct
    method Channel (line 56) | func (u TokenUsage) Channel() string {
  type Client (line 68) | type Client struct
    method SetAPIKey (line 148) | func (client *Client) SetAPIKey(apiKey, apiURL, customModel string) {
    method SetTimeout (line 164) | func (client *Client) SetTimeout(timeout time.Duration) {
    method CallWithMessages (line 169) | func (client *Client) CallWithMessages(systemPrompt, userPrompt string...
    method SetAuthHeader (line 209) | func (client *Client) SetAuthHeader(reqHeader http.Header) {
    method BuildMCPRequestBody (line 213) | func (client *Client) BuildMCPRequestBody(systemPrompt, userPrompt str...
    method MarshalRequestBody (line 256) | func (client *Client) MarshalRequestBody(requestBody map[string]any) (...
    method ParseMCPResponse (line 264) | func (client *Client) ParseMCPResponse(body []byte) (string, error) {
    method ParseMCPResponseFull (line 274) | func (client *Client) ParseMCPResponseFull(body []byte) (*LLMResponse,...
    method BuildUrl (line 315) | func (client *Client) BuildUrl() string {
    method BuildRequest (line 322) | func (client *Client) BuildRequest(url string, jsonData []byte) (*http...
    method Call (line 338) | func (client *Client) Call(systemPrompt, userPrompt string) (string, e...
    method String (line 392) | func (client *Client) String() string {
    method IsRetryableError (line 398) | func (client *Client) IsRetryableError(err error) bool {
    method CallWithRequest (line 414) | func (client *Client) CallWithRequest(req *Request) (string, error) {
    method CallWithRequestFull (line 460) | func (client *Client) CallWithRequestFull(req *Request) (*LLMResponse,...
    method callWithRequestFull (line 491) | func (client *Client) callWithRequestFull(req *Request) (*LLMResponse,...
    method callWithRequest (line 524) | func (client *Client) callWithRequest(req *Request) (string, error) {
    method BuildRequestBodyFromRequest (line 568) | func (client *Client) BuildRequestBodyFromRequest(req *Request) map[st...
    method CallWithRequestStream (line 665) | func (client *Client) CallWithRequestStream(req *Request, onChunk func...
  function New (line 89) | func New() AIClient {
  function NewClient (line 112) | func NewClient(opts ...ClientOption) AIClient {
  function ParseSSEStream (line 740) | func ParseSSEStream(body io.Reader, onChunk func(string), onLine func())...

FILE: mcp/client_test.go
  function TestNewClient_Default (line 14) | func TestNewClient_Default(t *testing.T) {
  function TestNewClient_WithOptions (line 43) | func TestNewClient_WithOptions(t *testing.T) {
  function TestClient_CallWithMessages_Success (line 78) | func TestClient_CallWithMessages_Success(t *testing.T) {
  function TestClient_CallWithMessages_NoAPIKey (line 117) | func TestClient_CallWithMessages_NoAPIKey(t *testing.T) {
  function TestClient_CallWithMessages_HTTPError (line 131) | func TestClient_CallWithMessages_HTTPError(t *testing.T) {
  function TestClient_Retry_Success (line 153) | func TestClient_Retry_Success(t *testing.T) {
  function TestClient_Retry_NonRetryableError (line 213) | func TestClient_Retry_NonRetryableError(t *testing.T) {
  function TestClient_BuildMCPRequestBody (line 241) | func TestClient_BuildMCPRequestBody(t *testing.T) {
  function TestClient_BuildUrl (line 273) | func TestClient_BuildUrl(t *testing.T) {
  function TestClient_SetAuthHeader (line 311) | func TestClient_SetAuthHeader(t *testing.T) {
  function TestClient_IsRetryableError (line 324) | func TestClient_IsRetryableError(t *testing.T) {
  function TestClient_SetTimeout (line 374) | func TestClient_SetTimeout(t *testing.T) {
  function TestClient_String (line 390) | func TestClient_String(t *testing.T) {
  function contains (line 408) | func contains(s, substr string) bool {
  function findSubstring (line 412) | func findSubstring(s, substr string) bool {

FILE: mcp/config.go
  type Config (line 14) | type Config struct
  function DefaultConfig (line 41) | func DefaultConfig() *Config {
  function getEnvInt (line 58) | func getEnvInt(key string, defaultValue int) int {
  function getEnvString (line 68) | func getEnvString(key string, defaultValue string) string {

FILE: mcp/config_usage_test.go
  function TestConfig_MaxRetries_IsUsed (line 17) | func TestConfig_MaxRetries_IsUsed(t *testing.T) {
  function TestConfig_Temperature_IsUsed (line 69) | func TestConfig_Temperature_IsUsed(t *testing.T) {
  function TestConfig_RetryWaitBase_IsUsed (line 124) | func TestConfig_RetryWaitBase_IsUsed(t *testing.T) {
  function TestConfig_RetryableErrors_IsUsed (line 188) | func TestConfig_RetryableErrors_IsUsed(t *testing.T) {
  function TestConfig_DefaultValues (line 242) | func TestConfig_DefaultValues(t *testing.T) {

FILE: mcp/context_guard.go
  function estimateMessageTokens (line 11) | func estimateMessageTokens(messages []map[string]string) int {
  function estimateMessageTokensAny (line 23) | func estimateMessageTokensAny(messages []map[string]any) int {
  function truncateMessages (line 42) | func truncateMessages(messages []map[string]string, maxContext, maxToken...
  function truncateMessagesAny (line 97) | func truncateMessagesAny(messages []map[string]any, maxContext, maxToken...

FILE: mcp/context_guard_test.go
  function TestEstimateMessageTokens (line 8) | func TestEstimateMessageTokens(t *testing.T) {
  function TestTruncateMessages_NoTruncationNeeded (line 25) | func TestTruncateMessages_NoTruncationNeeded(t *testing.T) {
  function TestTruncateMessages_NoLimit (line 39) | func TestTruncateMessages_NoLimit(t *testing.T) {
  function TestTruncateMessages_TruncatesOldest (line 52) | func TestTruncateMessages_TruncatesOldest(t *testing.T) {
  function TestTruncateMessages_PreservesSystemMessages (line 86) | func TestTruncateMessages_PreservesSystemMessages(t *testing.T) {
  function TestTruncateMessages_KeepsAtLeastOneNonSystem (line 108) | func TestTruncateMessages_KeepsAtLeastOneNonSystem(t *testing.T) {

FILE: mcp/examples_test.go
  function Example_backward_compatible (line 16) | func Example_backward_compatible() {
  function Example_deepseek_backward_compatible (line 26) | func Example_deepseek_backward_compatible() {
  function Example_new_client_basic (line 39) | func Example_new_client_basic() {
  function Example_new_client_with_options (line 56) | func Example_new_client_with_options() {
  type CustomLogger (line 75) | type CustomLogger struct
    method Debugf (line 77) | func (l *CustomLogger) Debugf(format string, args ...any) {
    method Infof (line 81) | func (l *CustomLogger) Infof(format string, args ...any) {
    method Warnf (line 85) | func (l *CustomLogger) Warnf(format string, args ...any) {
    method Errorf (line 89) | func (l *CustomLogger) Errorf(format string, args ...any) {
  function Example_custom_logger (line 93) | func Example_custom_logger() {
  function Example_no_logger_for_testing (line 106) | func Example_no_logger_for_testing() {
  function Example_custom_http_client (line 120) | func Example_custom_http_client() {
  function Example_deepseek_new_api (line 143) | func Example_deepseek_new_api() {
  function Example_qwen_new_api (line 165) | func Example_qwen_new_api() {
  function Example_trader_migration (line 186) | func Example_trader_migration() {
  type MockHTTPClient (line 221) | type MockHTTPClient struct
    method Do (line 225) | func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
  function Example_testing_with_mock (line 233) | func Example_testing_with_mock() {
  function Example_environment_specific (line 246) | func Example_environment_specific() {
  function Example_real_world_usage (line 268) | func Example_real_world_usage() {

FILE: mcp/hooks.go
  type ClientHooks (line 13) | type ClientHooks interface

FILE: mcp/interface.go
  type ClientEmbedder (line 9) | type ClientEmbedder interface
  type AIClient (line 14) | type AIClient interface

FILE: mcp/logger.go
  type Logger (line 6) | type Logger interface
  type noopLogger (line 14) | type noopLogger struct
    method Debugf (line 16) | func (l *noopLogger) Debugf(format string, args ...any) {}
    method Infof (line 17) | func (l *noopLogger) Infof(format string, args ...any)  {}
    method Warnf (line 18) | func (l *noopLogger) Warnf(format string, args ...any)  {}
    method Errorf (line 19) | func (l *noopLogger) Errorf(format string, args ...any) {}
  function NewNoopLogger (line 22) | func NewNoopLogger() Logger {

FILE: mcp/mock_test.go
  type MockLogger (line 17) | type MockLogger struct
    method Debugf (line 38) | func (m *MockLogger) Debugf(format string, args ...any) {
    method Infof (line 42) | func (m *MockLogger) Infof(format string, args ...any) {
    method Warnf (line 46) | func (m *MockLogger) Warnf(format string, args ...any) {
    method Errorf (line 50) | func (m *MockLogger) Errorf(format string, args ...any) {
    method log (line 54) | func (m *MockLogger) log(level, format string, args ...any) {
    method GetLogs (line 72) | func (m *MockLogger) GetLogs() []LogEntry {
    method GetLogsByLevel (line 79) | func (m *MockLogger) GetLogsByLevel(level string) []LogEntry {
    method Clear (line 93) | func (m *MockLogger) Clear() {
    method HasLog (line 100) | func (m *MockLogger) HasLog(level, message string) bool {
  type LogEntry (line 24) | type LogEntry struct
  function NewMockLogger (line 31) | func NewMockLogger() *MockLogger {
  type MockHTTPClient (line 117) | type MockHTTPClient struct
    method ToHTTPClient (line 138) | func (m *MockHTTPClient) ToHTTPClient() *http.Client {
    method RoundTrip (line 145) | func (m *MockHTTPClient) RoundTrip(req *http.Request) (*http.Response,...
    method GetRequests (line 173) | func (m *MockHTTPClient) GetRequests() []*http.Request {
    method GetLastRequest (line 180) | func (m *MockHTTPClient) GetLastRequest() *http.Request {
    method Reset (line 191) | func (m *MockHTTPClient) Reset() {
    method SetSuccessResponse (line 198) | func (m *MockHTTPClient) SetSuccessResponse(content string) {
    method SetErrorResponse (line 208) | func (m *MockHTTPClient) SetErrorResponse(statusCode int, message stri...
    method SetNetworkError (line 218) | func (m *MockHTTPClient) SetNetworkError(err error) {
  function NewMockHTTPClient (line 130) | func NewMockHTTPClient() *MockHTTPClient {
  type MockClientHooks (line 230) | type MockClientHooks struct
    method BuildMCPRequestBody (line 250) | func (m *MockClientHooks) BuildMCPRequestBody(systemPrompt, userPrompt...
    method BuildUrl (line 264) | func (m *MockClientHooks) BuildUrl() string {
    method SetAuthHeader (line 272) | func (m *MockClientHooks) SetAuthHeader(headers http.Header) {
    method MarshalRequestBody (line 277) | func (m *MockClientHooks) MarshalRequestBody(body map[string]any) ([]b...
    method ParseMCPResponse (line 285) | func (m *MockClientHooks) ParseMCPResponse(body []byte) (string, error) {
    method ParseMCPResponseFull (line 293) | func (m *MockClientHooks) ParseMCPResponseFull(body []byte) (*LLMRespo...
    method IsRetryableError (line 301) | func (m *MockClientHooks) IsRetryableError(err error) bool {
    method BuildRequest (line 309) | func (m *MockClientHooks) BuildRequest(url string, jsonData []byte) (*...
    method Call (line 316) | func (m *MockClientHooks) Call(systemPrompt, userPrompt string) (strin...
    method BuildRequestBodyFromRequest (line 320) | func (m *MockClientHooks) BuildRequestBodyFromRequest(req *Request) ma...
  function NewMockClientHooks (line 246) | func NewMockClientHooks() *MockClientHooks {

FILE: mcp/options.go
  type ClientOption (line 9) | type ClientOption
  function WithLogger (line 19) | func WithLogger(logger Logger) ClientOption {
  function WithHTTPClient (line 34) | func WithHTTPClient(client *http.Client) ClientOption {
  function WithTimeout (line 48) | func WithTimeout(timeout time.Duration) ClientOption {
  function WithMaxRetries (line 59) | func WithMaxRetries(maxRetries int) ClientOption {
  function WithRetryWaitBase (line 69) | func WithRetryWaitBase(waitTime time.Duration) ClientOption {
  function WithMaxTokens (line 83) | func WithMaxTokens(maxTokens int) ClientOption {
  function WithMaxContext (line 96) | func WithMaxContext(maxContext int) ClientOption {
  function WithTemperature (line 106) | func WithTemperature(temperature float64) ClientOption {
  function WithAPIKey (line 117) | func WithAPIKey(apiKey string) ClientOption {
  function WithBaseURL (line 124) | func WithBaseURL(baseURL string) ClientOption {
  function WithModel (line 131) | func WithModel(model string) ClientOption {
  function WithProvider (line 138) | func WithProvider(provider string) ClientOption {
  function WithUseFullURL (line 145) | func WithUseFullURL(useFullURL bool) ClientOption {
  function WithDeepSeekConfig (line 159) | func WithDeepSeekConfig(apiKey string) ClientOption {
  function WithQwenConfig (line 172) | func WithQwenConfig(apiKey string) ClientOption {
  function WithMiniMaxConfig (line 186) | func WithMiniMaxConfig(apiKey string) ClientOption {

FILE: mcp/options_test.go
  function TestWithProvider (line 13) | func TestWithProvider(t *testing.T) {
  function TestWithAPIKey (line 22) | func TestWithAPIKey(t *testing.T) {
  function TestWithBaseURL (line 31) | func TestWithBaseURL(t *testing.T) {
  function TestWithModel (line 40) | func TestWithModel(t *testing.T) {
  function TestWithMaxTokens (line 49) | func TestWithMaxTokens(t *testing.T) {
  function TestWithTemperature (line 58) | func TestWithTemperature(t *testing.T) {
  function TestWithUseFullURL (line 67) | func TestWithUseFullURL(t *testing.T) {
  function TestWithMaxRetries (line 76) | func TestWithMaxRetries(t *testing.T) {
  function TestWithTimeout (line 85) | func TestWithTimeout(t *testing.T) {
  function TestWithLogger (line 94) | func TestWithLogger(t *testing.T) {
  function TestWithHTTPClient (line 104) | func TestWithHTTPClient(t *testing.T) {
  function TestWithDeepSeekConfig (line 122) | func TestWithDeepSeekConfig(t *testing.T) {
  function TestWithQwenConfig (line 143) | func TestWithQwenConfig(t *testing.T) {
  function TestMultipleOptions (line 168) | func TestMultipleOptions(t *testing.T) {
  function TestOptionsOverride (line 223) | func TestOptionsOverride(t *testing.T) {
  function TestOptionsWithNewClient (line 256) | func TestOptionsWithNewClient(t *testing.T) {

FILE: mcp/payment/blockrun_base.go
  constant DefaultBlockRunBaseURL (line 22) | DefaultBlockRunBaseURL = "https://blockrun.ai"
  constant DefaultBlockRunModel (line 23) | DefaultBlockRunModel   = "gpt-5.4"
  constant BlockRunChatEndpoint (line 24) | BlockRunChatEndpoint   = "/api/v1/chat/completions"
  constant BaseUSDCContract (line 25) | BaseUSDCContract       = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
  constant BaseChainID (line 26) | BaseChainID      int64 = 8453
  constant BaseNetwork (line 27) | BaseNetwork            = "eip155:8453"
  function init (line 36) | func init() {
  function keccak256String (line 42) | func keccak256String(s string) []byte {
  function keccak256Bytes (line 48) | func keccak256Bytes(data ...[]byte) []byte {
  type BlockRunBaseClient (line 57) | type BlockRunBaseClient struct
    method BaseClient (line 62) | func (c *BlockRunBaseClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 89) | func (c *BlockRunBaseClient) SetAPIKey(apiKey string, customURL string...
    method SetAuthHeader (line 108) | func (c *BlockRunBaseClient) SetAuthHeader(h http.Header) { X402SetAut...
    method Call (line 110) | func (c *BlockRunBaseClient) Call(systemPrompt, userPrompt string) (st...
    method CallWithRequestFull (line 114) | func (c *BlockRunBaseClient) CallWithRequestFull(req *mcp.Request) (*m...
    method signPayment (line 119) | func (c *BlockRunBaseClient) signPayment(paymentHeaderB64 string) (str...
    method BuildUrl (line 346) | func (c *BlockRunBaseClient) BuildUrl() string {
    method BuildRequest (line 350) | func (c *BlockRunBaseClient) BuildRequest(url string, jsonData []byte)...
  function NewBlockRunBaseClient (line 65) | func NewBlockRunBaseClient() mcp.AIClient {
  function NewBlockRunBaseClientWithOptions (line 70) | func NewBlockRunBaseClientWithOptions(opts ...mcp.ClientOption) mcp.AICl...
  function SignX402Payment (line 125) | func SignX402Payment(privateKey *ecdsa.PrivateKey, senderAddr string, op...
  function buildDomainSeparatorDynamic (line 236) | func buildDomainSeparatorDynamic(name, version, network, asset string) (...
  function buildTransferWithAuthHashDynamic (line 268) | func buildTransferWithAuthHashDynamic(from, to string, value *big.Int, v...
  function hexToAddress (line 297) | func hexToAddress(s string) ([]byte, error) {
  function hexToBytes32 (line 309) | func hexToBytes32(s string) ([]byte, error) {
  function parseBigInt (line 321) | func parseBigInt(s string) (*big.Int, error) {
  function leftPad32 (line 336) | func leftPad32(b []byte) []byte {

FILE: mcp/payment/blockrun_sol.go
  constant DefaultBlockRunSolURL (line 20) | DefaultBlockRunSolURL = "https://sol.blockrun.ai"
  constant SolanaUSDCMint (line 21) | SolanaUSDCMint        = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
  constant SolanaNetwork (line 22) | SolanaNetwork         = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
  constant SolanaMainnetRPC (line 23) | SolanaMainnetRPC      = "https://api.mainnet-beta.solana.com"
  constant computeUnitLimit (line 26) | computeUnitLimit = uint32(8000)
  constant computeUnitPrice (line 27) | computeUnitPrice = uint64(1)
  function init (line 30) | func init() {
  type BlockRunSolClient (line 37) | type BlockRunSolClient struct
    method BaseClient (line 42) | func (c *BlockRunSolClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 69) | func (c *BlockRunSolClient) SetAPIKey(apiKey string, customURL string,...
    method SetAuthHeader (line 87) | func (c *BlockRunSolClient) SetAuthHeader(h http.Header) { X402SetAuth...
    method Call (line 89) | func (c *BlockRunSolClient) Call(systemPrompt, userPrompt string) (str...
    method CallWithRequestFull (line 93) | func (c *BlockRunSolClient) CallWithRequestFull(req *mcp.Request) (*mc...
    method signSolanaPayment (line 98) | func (c *BlockRunSolClient) signSolanaPayment(paymentHeaderB64 string)...
    method buildSolanaTransferTx (line 187) | func (c *BlockRunSolClient) buildSolanaTransferTx(recipient, feePayer,...
    method BuildUrl (line 270) | func (c *BlockRunSolClient) BuildUrl() string {
    method BuildRequest (line 274) | func (c *BlockRunSolClient) BuildRequest(url string, jsonData []byte) ...
  function NewBlockRunSolClient (line 45) | func NewBlockRunSolClient() mcp.AIClient {
  function NewBlockRunSolClientWithOptions (line 50) | func NewBlockRunSolClientWithOptions(opts ...mcp.ClientOption) mcp.AICli...

FILE: mcp/payment/claw402.go
  constant DefaultClaw402URL (line 15) | DefaultClaw402URL   = "https://claw402.ai"
  constant DefaultClaw402Model (line 16) | DefaultClaw402Model = "deepseek"
  function init (line 44) | func init() {
  type Claw402Client (line 53) | type Claw402Client struct
    method BaseClient (line 59) | func (c *Claw402Client) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 86) | func (c *Claw402Client) SetAPIKey(apiKey string, _ string, customModel...
    method resolveEndpoint (line 114) | func (c *Claw402Client) resolveEndpoint() string {
    method SetAuthHeader (line 125) | func (c *Claw402Client) SetAuthHeader(h http.Header) { X402SetAuthHead...
    method Call (line 127) | func (c *Claw402Client) Call(systemPrompt, userPrompt string) (string,...
    method CallWithRequestFull (line 131) | func (c *Claw402Client) CallWithRequestFull(req *mcp.Request) (*mcp.LL...
    method signPayment (line 136) | func (c *Claw402Client) signPayment(paymentHeaderB64 string) (string, ...
    method BuildMCPRequestBody (line 142) | func (c *Claw402Client) BuildMCPRequestBody(systemPrompt, userPrompt s...
    method BuildRequestBodyFromRequest (line 149) | func (c *Claw402Client) BuildRequestBodyFromRequest(req *mcp.Request) ...
    method ParseMCPResponse (line 156) | func (c *Claw402Client) ParseMCPResponse(body []byte) (string, error) {
    method ParseMCPResponseFull (line 163) | func (c *Claw402Client) ParseMCPResponseFull(body []byte) (*mcp.LLMRes...
    method BuildUrl (line 171) | func (c *Claw402Client) BuildUrl() string {
    method BuildRequest (line 175) | func (c *Claw402Client) BuildRequest(url string, jsonData []byte) (*ht...
  function NewClaw402Client (line 62) | func NewClaw402Client() mcp.AIClient {
  function NewClaw402ClientWithOptions (line 67) | func NewClaw402ClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {

FILE: mcp/payment/x402.go
  constant X402MaxPaymentRetries (line 22) | X402MaxPaymentRetries = 5
  constant X402RetryBaseWait (line 25) | X402RetryBaseWait = 3 * time.Second
  constant X402Timeout (line 30) | X402Timeout = 5 * time.Minute
  type X402v2PaymentRequired (line 36) | type X402v2PaymentRequired struct
  type X402AcceptOption (line 43) | type X402AcceptOption struct
  type X402Resource (line 54) | type X402Resource struct
  type X402SignFunc (line 62) | type X402SignFunc
  function X402DecodeHeader (line 68) | func X402DecodeHeader(b64 string) ([]byte, error) {
  function SignBasePaymentHeader (line 81) | func SignBasePaymentHeader(privateKey *ecdsa.PrivateKey, paymentHeaderB6...
  function DoX402Request (line 104) | func DoX402Request(
  function DoX402RequestStream (line 239) | func DoX402RequestStream(
  constant x402StreamIdleTimeout (line 372) | x402StreamIdleTimeout = 90 * time.Second
  function X402CallStream (line 380) | func X402CallStream(c *mcp.Client, signFn X402SignFunc, tag string, syst...
  function X402BuildRequest (line 467) | func X402BuildRequest(url string, jsonData []byte) (*http.Request, error) {
  function X402SetAuthHeader (line 478) | func X402SetAuthHeader(_ http.Header) {}
  function X402Call (line 481) | func X402Call(c *mcp.Client, signFn X402SignFunc, tag string, systemProm...
  function X402CallFull (line 500) | func X402CallFull(c *mcp.Client, signFn X402SignFunc, tag string, req *m...

FILE: mcp/provider/claude.go
  constant DefaultClaudeBaseURL (line 28) | DefaultClaudeBaseURL = "https://api.anthropic.com/v1"
  constant DefaultClaudeModel (line 29) | DefaultClaudeModel   = "claude-opus-4-6"
  function init (line 32) | func init() {
  type ClaudeClient (line 41) | type ClaudeClient struct
    method BaseClient (line 45) | func (c *ClaudeClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 68) | func (c *ClaudeClient) SetAPIKey(apiKey, customURL, customModel string) {
    method SetAuthHeader (line 84) | func (c *ClaudeClient) SetAuthHeader(h http.Header) {
    method BuildUrl (line 90) | func (c *ClaudeClient) BuildUrl() string {
    method BuildMCPRequestBody (line 96) | func (c *ClaudeClient) BuildMCPRequestBody(systemPrompt, userPrompt st...
    method BuildRequestBodyFromRequest (line 109) | func (c *ClaudeClient) BuildRequestBodyFromRequest(req *mcp.Request) m...
    method ParseMCPResponse (line 227) | func (c *ClaudeClient) ParseMCPResponse(body []byte) (string, error) {
    method ParseMCPResponseFull (line 237) | func (c *ClaudeClient) ParseMCPResponseFull(body []byte) (*mcp.LLMResp...
  function NewClaudeClient (line 48) | func NewClaudeClient() mcp.AIClient {
  function NewClaudeClientWithOptions (line 53) | func NewClaudeClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {
  function ConvertMessagesToAnthropic (line 165) | func ConvertMessagesToAnthropic(msgs []mcp.Message) []map[string]any {

FILE: mcp/provider/deepseek.go
  function init (line 9) | func init() {
  type DeepSeekClient (line 15) | type DeepSeekClient struct
    method BaseClient (line 19) | func (c *DeepSeekClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 47) | func (dsClient *DeepSeekClient) SetAPIKey(apiKey string, customURL str...
    method SetAuthHeader (line 67) | func (dsClient *DeepSeekClient) SetAuthHeader(reqHeaders http.Header) {
  function NewDeepSeekClient (line 24) | func NewDeepSeekClient() mcp.AIClient {
  function NewDeepSeekClientWithOptions (line 29) | func NewDeepSeekClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {

FILE: mcp/provider/gemini.go
  constant DefaultGeminiBaseURL (line 10) | DefaultGeminiBaseURL = "https://generativelanguage.googleapis.com/v1beta...
  constant DefaultGeminiModel (line 11) | DefaultGeminiModel   = "gemini-3-pro-preview"
  function init (line 14) | func init() {
  type GeminiClient (line 20) | type GeminiClient struct
    method BaseClient (line 24) | func (c *GeminiClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 50) | func (c *GeminiClient) SetAPIKey(apiKey string, customURL string, cust...
    method SetAuthHeader (line 71) | func (c *GeminiClient) SetAuthHeader(reqHeaders http.Header) {
  function NewGeminiClient (line 27) | func NewGeminiClient() mcp.AIClient {
  function NewGeminiClientWithOptions (line 32) | func NewGeminiClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {

FILE: mcp/provider/grok.go
  constant DefaultGrokBaseURL (line 10) | DefaultGrokBaseURL = "https://api.x.ai/v1"
  constant DefaultGrokModel (line 11) | DefaultGrokModel   = "grok-3-latest"
  function init (line 14) | func init() {
  type GrokClient (line 20) | type GrokClient struct
    method BaseClient (line 24) | func (c *GrokClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 50) | func (c *GrokClient) SetAPIKey(apiKey string, customURL string, custom...
    method SetAuthHeader (line 71) | func (c *GrokClient) SetAuthHeader(reqHeaders http.Header) {
  function NewGrokClient (line 27) | func NewGrokClient() mcp.AIClient {
  function NewGrokClientWithOptions (line 32) | func NewGrokClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {

FILE: mcp/provider/kimi.go
  constant DefaultKimiBaseURL (line 10) | DefaultKimiBaseURL = "https://api.moonshot.ai/v1"
  constant DefaultKimiModel (line 11) | DefaultKimiModel   = "moonshot-v1-auto"
  function init (line 14) | func init() {
  type KimiClient (line 20) | type KimiClient struct
    method BaseClient (line 24) | func (c *KimiClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 50) | func (c *KimiClient) SetAPIKey(apiKey string, customURL string, custom...
    method SetAuthHeader (line 71) | func (c *KimiClient) SetAuthHeader(reqHeaders http.Header) {
  function NewKimiClient (line 27) | func NewKimiClient() mcp.AIClient {
  function NewKimiClientWithOptions (line 32) | func NewKimiClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {

FILE: mcp/provider/minimax.go
  constant DefaultMiniMaxBaseURL (line 10) | DefaultMiniMaxBaseURL = "https://api.minimax.io/v1"
  constant DefaultMiniMaxModel (line 11) | DefaultMiniMaxModel   = "MiniMax-M2.5"
  function init (line 14) | func init() {
  type MiniMaxClient (line 20) | type MiniMaxClient struct
    method BaseClient (line 24) | func (c *MiniMaxClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 50) | func (c *MiniMaxClient) SetAPIKey(apiKey string, customURL string, cus...
    method SetAuthHeader (line 71) | func (c *MiniMaxClient) SetAuthHeader(reqHeaders http.Header) {
  function NewMiniMaxClient (line 27) | func NewMiniMaxClient() mcp.AIClient {
  function NewMiniMaxClientWithOptions (line 32) | func NewMiniMaxClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {

FILE: mcp/provider/openai.go
  constant DefaultOpenAIBaseURL (line 10) | DefaultOpenAIBaseURL = "https://api.openai.com/v1"
  constant DefaultOpenAIModel (line 11) | DefaultOpenAIModel   = "gpt-5.4"
  function init (line 14) | func init() {
  type OpenAIClient (line 20) | type OpenAIClient struct
    method BaseClient (line 24) | func (c *OpenAIClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 50) | func (c *OpenAIClient) SetAPIKey(apiKey string, customURL string, cust...
    method SetAuthHeader (line 71) | func (c *OpenAIClient) SetAuthHeader(reqHeaders http.Header) {
  function NewOpenAIClient (line 27) | func NewOpenAIClient() mcp.AIClient {
  function NewOpenAIClientWithOptions (line 32) | func NewOpenAIClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {

FILE: mcp/provider/options_test.go
  function TestOptionsWithDeepSeekClient (line 9) | func TestOptionsWithDeepSeekClient(t *testing.T) {
  function TestOptionsWithQwenClient (line 47) | func TestOptionsWithQwenClient(t *testing.T) {

FILE: mcp/provider/qwen.go
  constant DefaultQwenBaseURL (line 10) | DefaultQwenBaseURL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
  constant DefaultQwenModel (line 11) | DefaultQwenModel   = "qwen3-max"
  function init (line 14) | func init() {
  type QwenClient (line 20) | type QwenClient struct
    method BaseClient (line 24) | func (c *QwenClient) BaseClient() *mcp.Client { return c.Client }
    method SetAPIKey (line 52) | func (qwenClient *QwenClient) SetAPIKey(apiKey string, customURL strin...
    method SetAuthHeader (line 72) | func (qwenClient *QwenClient) SetAuthHeader(reqHeaders http.Header) {
  function NewQwenClient (line 29) | func NewQwenClient() mcp.AIClient {
  function NewQwenClientWithOptions (line 34) | func NewQwenClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient {

FILE: mcp/providers.go
  constant ProviderDeepSeek (line 7) | ProviderDeepSeek = "deepseek"
  constant ProviderOpenAI (line 8) | ProviderOpenAI   = "openai"
  constant ProviderClaude (line 9) | ProviderClaude   = "claude"
  constant ProviderQwen (line 10) | ProviderQwen     = "qwen"
  constant ProviderGemini (line 11) | ProviderGemini   = "gemini"
  constant ProviderGrok (line 12) | ProviderGrok     = "grok"
  constant ProviderKimi (line 13) | ProviderKimi     = "kimi"
  constant ProviderMiniMax (line 14) | ProviderMiniMax  = "minimax"
  constant ProviderBlockRunBase (line 16) | ProviderBlockRunBase = "blockrun-base"
  constant ProviderBlockRunSol (line 17) | ProviderBlockRunSol  = "blockrun-sol"
  constant ProviderClaw402 (line 18) | ProviderClaw402      = "claw402"
  constant DefaultDeepSeekBaseURL (line 21) | DefaultDeepSeekBaseURL = "https://api.deepseek.com"
  constant DefaultDeepSeekModel (line 22) | DefaultDeepSeekModel   = "deepseek-chat"
  constant DefaultQwenBaseURL (line 25) | DefaultQwenBaseURL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
  constant DefaultQwenModel (line 26) | DefaultQwenModel   = "qwen3-max"
  constant DefaultMiniMaxBaseURL (line 29) | DefaultMiniMaxBaseURL = "https://api.minimax.io/v1"
  constant DefaultMiniMaxModel (line 30) | DefaultMiniMaxModel   = "MiniMax-M2.5"

FILE: mcp/registry.go
  function RegisterProvider (line 8) | func RegisterProvider(name string, factory func(...ClientOption) AIClien...
  function NewAIClientByProvider (line 14) | func NewAIClientByProvider(name string, opts ...ClientOption) AIClient {

FILE: mcp/request.go
  type Message (line 6) | type Message struct
  type ToolCall (line 14) | type ToolCall struct
  type ToolCallFunction (line 21) | type ToolCallFunction struct
  type LLMResponse (line 29) | type LLMResponse struct
  type Tool (line 35) | type Tool struct
  type FunctionDef (line 41) | type FunctionDef struct
  type Request (line 48) | type Request struct
  function NewMessage (line 68) | func NewMessage(role, content string) Message {
  function NewSystemMessage (line 76) | func NewSystemMessage(content string) Message {
  function NewUserMessage (line 84) | func NewUserMessage(content string) Message {
  function NewAssistantMessage (line 92) | func NewAssistantMessage(content string) Message {

FILE: mcp/request_builder.go
  type RequestBuilder (line 8) | type RequestBuilder struct
    method WithModel (line 42) | func (b *RequestBuilder) WithModel(model string) *RequestBuilder {
    method WithStream (line 48) | func (b *RequestBuilder) WithStream(stream bool) *RequestBuilder {
    method WithSystemPrompt (line 58) | func (b *RequestBuilder) WithSystemPrompt(prompt string) *RequestBuild...
    method WithUserPrompt (line 66) | func (b *RequestBuilder) WithUserPrompt(prompt string) *RequestBuilder {
    method AddSystemMessage (line 74) | func (b *RequestBuilder) AddSystemMessage(content string) *RequestBuil...
    method AddUserMessage (line 79) | func (b *RequestBuilder) AddUserMessage(content string) *RequestBuilder {
    method AddAssistantMessage (line 84) | func (b *RequestBuilder) AddAssistantMessage(content string) *RequestB...
    method AddMessage (line 92) | func (b *RequestBuilder) AddMessage(role, content string) *RequestBuil...
    method AddMessages (line 100) | func (b *RequestBuilder) AddMessages(messages ...Message) *RequestBuil...
    method AddConversationHistory (line 106) | func (b *RequestBuilder) AddConversationHistory(history []Message) *Re...
    method ClearMessages (line 112) | func (b *RequestBuilder) ClearMessages() *RequestBuilder {
    method WithTemperature (line 123) | func (b *RequestBuilder) WithTemperature(t float64) *RequestBuilder {
    method WithMaxTokens (line 138) | func (b *RequestBuilder) WithMaxTokens(tokens int) *RequestBuilder {
    method WithTopP (line 147) | func (b *RequestBuilder) WithTopP(p float64) *RequestBuilder {
    method WithFrequencyPenalty (line 156) | func (b *RequestBuilder) WithFrequencyPenalty(penalty float64) *Reques...
    method WithPresencePenalty (line 165) | func (b *RequestBuilder) WithPresencePenalty(penalty float64) *Request...
    method WithStopSequences (line 174) | func (b *RequestBuilder) WithStopSequences(sequences []string) *Reques...
    method AddStopSequence (line 180) | func (b *RequestBuilder) AddStopSequence(sequence string) *RequestBuil...
    method AddTool (line 192) | func (b *RequestBuilder) AddTool(tool Tool) *RequestBuilder {
    method AddFunction (line 198) | func (b *RequestBuilder) AddFunction(name, description string, paramet...
    method WithToolChoice (line 215) | func (b *RequestBuilder) WithToolChoice(choice string) *RequestBuilder {
    method Build (line 225) | func (b *RequestBuilder) Build() (*Request, error) {
    method MustBuild (line 263) | func (b *RequestBuilder) MustBuild() *Request {
  function NewRequestBuilder (line 30) | func NewRequestBuilder() *RequestBuilder {
  function ForChat (line 276) | func ForChat() *RequestBuilder {
  function ForCodeGeneration (line 288) | func ForCodeGeneration() *RequestBuilder {
  function ForCreativeWriting (line 302) | func ForCreativeWriting() *RequestBuilder {

FILE: mcp/request_builder_test.go
  function TestRequestBuilder_BasicUsage (line 12) | func TestRequestBuilder_BasicUsage(t *testing.T) {
  function TestRequestBuilder_EmptyMessages (line 35) | func TestRequestBuilder_EmptyMessages(t *testing.T) {
  function TestRequestBuilder_MultipleMessages (line 51) | func TestRequestBuilder_MultipleMessages(t *testing.T) {
  function TestRequestBuilder_AddConversationHistory (line 71) | func TestRequestBuilder_AddConversationHistory(t *testing.T) {
  function TestRequestBuilder_WithTemperature (line 91) | func TestRequestBuilder_WithTemperature(t *testing.T) {
  function TestRequestBuilder_WithMaxTokens (line 106) | func TestRequestBuilder_WithMaxTokens(t *testing.T) {
  function TestRequestBuilder_WithTopP (line 121) | func TestRequestBuilder_WithTopP(t *testing.T) {
  function TestRequestBuilder_WithPenalties (line 136) | func TestRequestBuilder_WithPenalties(t *testing.T) {
  function TestRequestBuilder_WithStopSequences (line 152) | func TestRequestBuilder_WithStopSequences(t *testing.T) {
  function TestRequestBuilder_AddTool (line 171) | func TestRequestBuilder_AddTool(t *testing.T) {
  function TestRequestBuilder_AddFunction (line 205) | func TestRequestBuilder_AddFunction(t *testing.T) {
  function TestRequestBuilder_ForChat (line 235) | func TestRequestBuilder_ForChat(t *testing.T) {
  function TestRequestBuilder_ForCodeGeneration (line 257) | func TestRequestBuilder_ForCodeGeneration(t *testing.T) {
  function TestRequestBuilder_ForCreativeWriting (line 271) | func TestRequestBuilder_ForCreativeWriting(t *testing.T) {
  function TestClient_CallWithRequest_Success (line 293) | func TestClient_CallWithRequest_Success(t *testing.T) {
  function TestClient_CallWithRequest_MultiRound (line 345) | func TestClient_CallWithRequest_MultiRound(t *testing.T) {
  function TestClient_CallWithRequest_WithTools (line 386) | func TestClient_CallWithRequest_WithTools(t *testing.T) {
  function TestClient_CallWithRequest_NoAPIKey (line 430) | func TestClient_CallWithRequest_NoAPIKey(t *testing.T) {
  function TestClient_CallWithRequest_UsesClientModel (line 448) | func TestClient_CallWithRequest_UsesClientModel(t *testing.T) {

FILE: provider/alpaca/kline.go
  constant DataAPIURL (line 15) | DataAPIURL = "https://data.alpaca.markets/v2"
  type Bar (line 19) | type Bar struct
  type BarsResponse (line 31) | type BarsResponse struct
  type Client (line 38) | type Client struct
    method GetBars (line 69) | func (c *Client) GetBars(ctx context.Context, symbol string, timeframe...
  function NewClient (line 45) | func NewClient() *Client {
  function NewClientWithKeys (line 57) | func NewClientWithKeys(apiKey, secretKey string) *Client {
  function MapTimeframe (line 134) | func MapTimeframe(interval string) string {

FILE: provider/coinank/base_coin.go
  method ListCoin (line 10) | func (c *CoinankClient) ListCoin(ctx context.Context, productType coinan...
  method ListSymbols (line 33) | func (c *CoinankClient) ListSymbols(ctx context.Context, exchange coinan...
  type SymbolResp (line 56) | type SymbolResp struct

FILE: provider/coinank/coinank_api/base_coin.go
  function BaseCoinSymbols (line 11) | func BaseCoinSymbols(ctx context.Context, exchangeName coinank_enum.Exch...
  type BaseCoinResponse (line 37) | type BaseCoinResponse struct

FILE: provider/coinank/coinank_api/base_coin_test.go
  function TestBaseCoinSymbolsNoArgs (line 9) | func TestBaseCoinSymbolsNoArgs(t *testing.T) {
  function TestBaseCoinSymbolsBTC (line 21) | func TestBaseCoinSymbolsBTC(t *testing.T) {
  function TestBaseCoinSymbolsBTCUSDT (line 33) | func TestBaseCoinSymbolsBTCUSDT(t *testing.T) {

FILE: provider/coinank/coinank_api/depth_ws.go
  constant MainDepthWsUrl (line 11) | MainDepthWsUrl = "wss://ws.coinank.com/wsDepth/wsKline"
  type DepthWs (line 13) | type DepthWs struct
    method Subscribe (line 32) | func (ws *DepthWs) Subscribe(symbol string, exchange coinank_enum.Exch...
    method UnSubscribe (line 50) | func (ws *DepthWs) UnSubscribe(symbol string, exchange coinank_enum.Ex...
    method Close (line 68) | func (ws *DepthWs) Close() error {
  function DepthWsConn (line 19) | func DepthWsConn(ctx context.Context) (*DepthWs, error) {
  function depth_ws (line 72) | func depth_ws(ctx context.Context) (*websocket.Conn, <-chan *WsResult[De...
  function depth_read (line 86) | func depth_read(conn *websocket.Conn, ch chan *WsResult[DepthV3]) {
  type DepthV3 (line 103) | type DepthV3 struct

FILE: provider/coinank/coinank_api/depth_ws_test.go
  function TestDepthWs (line 12) | func TestDepthWs(t *testing.T) {

FILE: provider/coinank/coinank_api/kline.go
  constant MainApiUrl (line 16) | MainApiUrl = "https://api.coinank.com"
  function Kline (line 19) | func Kline(ctx context.Context, symbol string, exchange coinank_enum.Exc...
  function get (line 55) | func get(ctx context.Context, path string, paramsMap map[string]string) ...

FILE: provider/coinank/coinank_api/kline_test.go
  function TestKline (line 12) | func TestKline(t *testing.T) {
  function TestKlineDaily (line 24) | func TestKlineDaily(t *testing.T) {

FILE: provider/coinank/coinank_api/kline_ws.go
  constant MainWsUrl (line 14) | MainWsUrl = "wss://ws.coinank.com/ws"
  type KlineWs (line 16) | type KlineWs struct
    method Subscribe (line 38) | func (ws *KlineWs) Subscribe(symbol string, exchange coinank_enum.Exch...
    method UnSubscribe (line 56) | func (ws *KlineWs) UnSubscribe(symbol string, exchange coinank_enum.Ex...
    method Close (line 74) | func (ws *KlineWs) Close() error {
  function WsConn (line 23) | func WsConn(ctx context.Context, needKline bool, needTicker bool) (*Klin...
  function ws (line 78) | func ws(ctx context.Context) (*websocket.Conn, <-chan string, error) {
  function read (line 92) | func read(conn *websocket.Conn, ch chan string) {
  function handleResponse (line 105) | func handleResponse(ch <-chan string, needKline bool, needTicker bool) (...
  function toInt64 (line 154) | func toInt64(v any) int64 {
  function toFloat64 (line 159) | func toFloat64(v any) float64 {
  type SubscribeInfo (line 173) | type SubscribeInfo struct
  type KlineTickers (line 178) | type KlineTickers struct
  type WsResult (line 198) | type WsResult struct

FILE: provider/coinank/coinank_api/kline_ws_test.go
  function TestKlineWs (line 12) | func TestKlineWs(t *testing.T) {

FILE: provider/coinank/coinank_enum/exchange.go
  type Exchange (line 3) | type Exchange
  constant Binance (line 6) | Binance     Exchange = "Binance"
  constant Huobi (line 7) | Huobi       Exchange = "Huobi"
  constant Okex (line 8) | Okex        Exchange = "Okex"
  constant Bitmex (line 9) | Bitmex      Exchange = "Bitmex"
  constant FTX (line 10) | FTX         Exchange = "FTX"
  constant Bybit (line 11) | Bybit       Exchange = "Bybit"
  constant Gate (line 12) | Gate        Exchange = "Gate"
  constant Bitget (line 13) | Bitget      Exchange = "Bitget"
  constant dYdX (line 14) | dYdX        Exchange = "dYdX"
  constant Deribit (line 15) | Deribit     Exchange = "Deribit"
  constant Kraken (line 16) | Kraken      Exchange = "Kraken"
  constant Bitfinex (line 17) | Bitfinex    Exchange = "Bitfinex"
  constant AAX (line 18) | AAX         Exchange = "AAX"
  constant CME (line 19) | CME         Exchange = "CME"
  constant Upbit (line 20) | Upbit       Exchange = "Upbit"
  constant Coinbase (line 21) | Coinbase    Exchange = "Coinbase"
  constant Hyperliquid (line 22) | Hyperliquid Exchange = "Hyperliquid"
  constant Bitunix (line 23) | Bitunix     Exchange = "Bitunix"
  constant Aster (line 24) | Aster       Exchange = "Aster"

FILE: provider/coinank/coinank_enum/instrument_agg_sort_by.go
  type InstrumentAggSortBy (line 3) | type InstrumentAggSortBy
  constant OpenInterest (line 6) | OpenInterest            InstrumentAggSortBy = "openInterest"
  constant Price (line 7) | Price                   InstrumentAggSortBy = "price"
  constant PriceChangeM5 (line 8) | PriceChangeM5           InstrumentAggSortBy = "priceChangeM5"
  constant PriceChangeM15 (line 9) | PriceChangeM15          InstrumentAggSortBy = "priceChangeM15"
  constant PriceChangeM30 (line 10) | PriceChangeM30          InstrumentAggSortBy = "priceChangeM30"
  constant PriceChangeH4 (line 11) | PriceChangeH4           InstrumentAggSortBy = "priceChangeH4"
  constant PriceChangeH24 (line 12) | PriceChangeH24          InstrumentAggSortBy = "priceChangeH24"
  constant PriceChangeH1 (line 13) | PriceChangeH1           InstrumentAggSortBy = "priceChangeH1"
  constant PriceChangeH6 (line 14) | PriceChangeH6           InstrumentAggSortBy = "priceChangeH6"
  constant PriceChangeH12 (line 15) | PriceChangeH12          InstrumentAggSortBy = "priceChangeH12"
  constant OpenInterestChM15 (line 16) | OpenInterestChM15       InstrumentAggSortBy = "openInterestChM15"
  constant OpenInterestChM5 (line 17) | OpenInterestChM5        InstrumentAggSortBy = "openInterestChM5"
  constant OpenInterestChM30 (line 18) | OpenInterestChM30       InstrumentAggSortBy = "openInterestChM30"
  constant OpenInterestCh1 (line 19) | OpenInterestCh1         InstrumentAggSortBy = "openInterestCh1"
  constant OpenInterestCh4 (line 20) | OpenInterestCh4         InstrumentAggSortBy = "openInterestCh4"
  constant OpenInterestCh24 (line 21) | OpenInterestCh24        InstrumentAggSortBy = "openInterestCh24"
  constant OpenInterestCh2D (line 22) | OpenInterestCh2D        InstrumentAggSortBy = "openInterestCh2D"
  constant OpenInterestCh3D (line 23) | OpenInterestCh3D        InstrumentAggSortBy = "openInterestCh3D"
  constant OpenInterestCh7D (line 24) | OpenInterestCh7D        InstrumentAggSortBy = "openInterestCh7D"
  constant LiquidationH1 (line 25) | LiquidationH1           InstrumentAggSortBy = "liquidationH1"
  constant LiquidationH1Long (line 26) | LiquidationH1Long       InstrumentAggSortBy = "liquidationH1Long"
  constant LiquidationH1Short (line 27) | LiquidationH1Short      InstrumentAggSortBy = "liquidationH1Short"
  constant LiquidationH4Long (line 28) | LiquidationH4Long       InstrumentAggSortBy = "liquidationH4Long"
  constant LiquidationH4Short (line 29) | LiquidationH4Short      InstrumentAggSortBy = "liquidationH4Short"
  constant LiquidationH24Short (line 30) | LiquidationH24Short     InstrumentAggSortBy = "liquidationH24Short"
  constant LiquidationH24Long (line 31) | LiquidationH24Long      InstrumentAggSortBy = "liquidationH24Long"
  constant LiquidationH4 (line 32) | LiquidationH4           InstrumentAggSortBy = "liquidationH4"
  constant FundingRate (line 33) | FundingRate             InstrumentAggSortBy = "fundingRate"
  constant LiquidationH12 (line 34) | LiquidationH12          InstrumentAggSortBy = "liquidationH12"
  constant LiquidationH12Long (line 35) | LiquidationH12Long      InstrumentAggSortBy = "liquidationH12Long"
  constant LiquidationH12Short (line 36) | LiquidationH12Short     InstrumentAggSortBy = "liquidationH12Short"
  constant LiquidationH24 (line 37) | LiquidationH24          InstrumentAggSortBy = "liquidationH24"
  constant Turnover24h (line 38) | Turnover24h             InstrumentAggSortBy = "turnover24h"
  constant TurnoverChg24h (line 39) | TurnoverChg24h          InstrumentAggSortBy = "turnoverChg24h"
  constant LongShortAccount (line 40) | LongShortAccount        InstrumentAggSortBy = "longShortAccount"
  constant LsPersonChg5m (line 41) | LsPersonChg5m           InstrumentAggSortBy = "lsPersonChg5m"
  constant LsPersonChg15m (line 42) | LsPersonChg15m          InstrumentAggSortBy = "lsPersonChg15m"
  constant LsPersonChg30m (line 43) | LsPersonChg30m          InstrumentAggSortBy = "lsPersonChg30m"
  constant LsPersonChg1h (line 44) | LsPersonChg1h           InstrumentAggSortBy = "lsPersonChg1h"
  constant LsPersonChg4h (line 45) | LsPersonChg4h           InstrumentAggSortBy = "lsPersonChg4h"
  constant LongShortPerson (line 46) | LongShortPerson         InstrumentAggSortBy = "longShortPerson"
  constant LongShortPosition (line 47) | LongShortPosition       InstrumentAggSortBy = "longShortPosition"
  constant LongShortRatio (line 48) | LongShortRatio          InstrumentAggSortBy = "longShortRatio"
  constant MarketCap (line 49) | MarketCap               InstrumentAggSortBy = "marketCap"
  constant MarketCapChange24H (line 50) | MarketCapChange24H      InstrumentAggSortBy = "marketCapChange24H"
  constant MarketCapChangeValue24H (line 51) | MarketCapChangeValue24H InstrumentAggSortBy = "marketCapChangeValue24H"
  constant TotalSupply (line 52) | TotalSupply             InstrumentAggSortBy = "totalSupply"
  constant MaxSupply (line 53) | MaxSupply               InstrumentAggSortBy = "maxSupply"
  constant CirculatingSupply (line 54) | CirculatingSupply       InstrumentAggSortBy = "circulatingSupply"
  constant Buy5m (line 55) | Buy5m                   InstrumentAggSortBy = "buy5m"
  constant Sell5m (line 56) | Sell5m                  InstrumentAggSortBy = "sell5m"
  constant Buy15m (line 57) | Buy15m                  InstrumentAggSortBy = "buy15m"
  constant Sell15m (line 58) | Sell15m                 InstrumentAggSortBy = "sell15m"
  constant Buy30m (line 59) | Buy30m                  InstrumentAggSortBy = "buy30m"
  constant Sell30m (line 60) | Sell30m                 InstrumentAggSortBy = "sell30m"
  constant Buy1h (line 61) | Buy1h                   InstrumentAggSortBy = "buy1h"
  constant Sell1h (line 62) | Sell1h                  InstrumentAggSortBy = "sell1h"
  constant Buy2h (line 63) | Buy2h                   InstrumentAggSortBy = "buy2h"
  constant Sell2h (line 64) | Sell2h                  InstrumentAggSortBy = "sell2h"
  constant Buy4h (line 65) | Buy4h                   InstrumentAggSortBy = "buy4h"
  constant Sell4h (line 66) | Sell4h                  InstrumentAggSortBy = "sell4h"
  constant Buy6h (line 67) | Buy6h                   InstrumentAggSortBy = "buy6h"
  constant Sell6h (line 68) | Sell6h                  InstrumentAggSortBy = "sell6h"
  constant Buy8h (line 69) | Buy8h                   InstrumentAggSortBy = "buy8h"
  constant Sell8h (line 70) | Sell8h                  InstrumentAggSortBy = "sell8h"
  constant Buy12h (line 71) | Buy12h                  InstrumentAggSortBy = "buy12h"
  constant Sell12h (line 72) | Sell12h                 InstrumentAggSortBy = "sell12h"
  constant Buy24h (line 73) | Buy24h                  InstrumentAggSortBy = "buy24h"
  constant Sell24h (line 74) | Sell24h                 InstrumentAggSortBy = "sell24h"
  constant TurnoverChg15m (line 75) | TurnoverChg15m          InstrumentAggSortBy = "turnoverChg15m"
  constant TurnoverChg30m (line 76) | TurnoverChg30m          InstrumentAggSortBy = "turnoverChg30m"
  constant TurnoverChg1h (line 77) | TurnoverChg1h           InstrumentAggSortBy = "turnoverChg1h"
  constant TurnoverChg4h (line 78) | TurnoverChg4h           InstrumentAggSortBy = "turnoverChg4h"
  constant VolatilityM5 (line 79) | VolatilityM5            InstrumentAggSortBy = "volatilityM5"
  constant VolatilityM15 (line 80) | VolatilityM15           InstrumentAggSortBy = "volatilityM15"
  constant VolatilityH1 (line 81) | VolatilityH1            InstrumentAggSortBy = "volatilityH1"
  constant OiVsMar (line 82) | OiVsMar                 InstrumentAggSortBy = "oiVsMar"
  constant OiVsVol (line 83) | OiVsVol                 InstrumentAggSortBy = "oiVsVol"
  constant VolVsMar (line 84) | VolVsMar                InstrumentAggSortBy = "volVsMar"

FILE: provider/coinank/coinank_enum/interval.go
  type Interval (line 3) | type Interval
  constant Second1 (line 6) | Second1  Interval = "1s"
  constant Second4 (line 7) | Second4  Interval = "4s"
  constant Second5 (line 8) | Second5  Interval = "5s"
  constant Second10 (line 9) | Second10 Interval = "10s"
  constant Second30 (line 10) | Second30 Interval = "30s"
  constant Minute1 (line 11) | Minute1  Interval = "1m"
  constant Minute3 (line 12) | Minute3  Interval = "3m"
  constant Minute5 (line 13) | Minute5  Interval = "5m"
  constant Minute10 (line 14) | Minute10 Interval = "10m"
  constant Minute15 (line 15) | Minute15 Interval = "15m"
  constant Minute30 (line 16) | Minute30 Interval = "30m"
  constant Hour1 (line 17) | Hour1    Interval = "1h"
  constant Hour2 (line 18) | Hour2    Interval = "2h"
  constant Hour4 (line 19) | Hour4    Interval = "4h"
  constant Hour5 (line 20) | Hour5    Interval = "5h"
  constant Hour6 (line 21) | Hour6    Interval = "6h"
  constant Hour8 (line 22) | Hour8    Interval = "8h"
  constant Hour12 (line 23) | Hour12   Interval = "12h"
  constant Day1 (line 24) | Day1     Interval = "1d"
  constant Day2 (line 25) | Day2     Interval = "2d"
  constant Day3 (line 26) | Day3     Interval = "3d"
  constant Day5 (line 27) | Day5     Interval = "5d"
  constant Week1 (line 28) | Week1    Interval = "1w"
  constant Week2 (line 29) | Week2    Interval = "2w"
  constant Month1 (line 30) | Month1   Interval = "1M"
  constant Month3 (line 31) | Month3   Interval = "3M"
  constant Month6 (line 32) | Month6   Interval = "6M"
  constant Year1 (line 33) | Year1    Interval = "1y"

FILE: provider/coinank/coinank_enum/product_type.go
  type ProductType (line 3) | type ProductType
  constant SWAP (line 5) | SWAP ProductType = "SWAP"
  constant SPOT (line 6) | SPOT ProductType = "SPOT"

FILE: provider/coinank/coinank_enum/side.go
  type Side (line 3) | type Side
  constant To (line 6) | To   Side = "to"
  constant From (line 7) | From Side = "from"

FILE: provider/coinank/coinank_enum/sort_type.go
  type SortType (line 3) | type SortType
  constant Asc (line 6) | Asc  SortType = "asc"
  constant Desc (line 7) | Desc SortType = "desc"

FILE: provider/coinank/coinank_http.go
  type CoinankClient (line 16) | type CoinankClient struct
    method Get (line 46) | func (c *CoinankClient) Get(ctx context.Context, path string, paramsMa...
    method Post (line 70) | func (c *CoinankClient) Post(ctx context.Context, path string, data an...
  type CoinankResponse (line 22) | type CoinankResponse struct
  type PageData (line 29) | type PageData struct
  function NewCoinankClient (line 41) | func NewCoinankClient(url, apikey string) *CoinankClient {

FILE: provider/coinank/instrument_agg_rank.go
  method VisualScreener (line 11) | func (c *CoinankClient) VisualScreener(ctx context.Context, interval coi...
  method OiRank (line 30) | func (c *CoinankClient) OiRank(ctx context.Context, sortBy coinank_enum....
  method LongShortRank (line 51) | func (c *CoinankClient) LongShortRank(ctx context.Context, sortBy coinan...
  method LiquidationRank (line 72) | func (c *CoinankClient) LiquidationRank(ctx context.Context, sortBy coin...
  method PriceRank (line 93) | func (c *CoinankClient) PriceRank(ctx context.Context, sortBy coinank_en...
  method VolumeRank (line 114) | func (c *CoinankClient) VolumeRank(ctx context.Context, sortBy coinank_e...
  method rankParam (line 134) | func (c *CoinankClient) rankParam(sortBy coinank_enum.InstrumentAggSortBy,
  type VisualScreenerResponse (line 156) | type VisualScreenerResponse struct
  type LongShortRankResponse (line 163) | type LongShortRankResponse struct
  type OiRankResponse (line 179) | type OiRankResponse struct
  type LiquidationRankResponse (line 200) | type LiquidationRankResponse struct
  type PriceRankResponse (line 221) | type PriceRankResponse struct
  type VolumeRankResponse (line 241) | type VolumeRankResponse struct

FILE: provider/coinank/instruments.go
  method GetLastPrice (line 10) | func (c *CoinankClient) GetLastPrice(ctx context.Context,
  method GetCoinMarketCap (line 32) | func (c *CoinankClient) GetCoinMarketCap(ctx context.Context,
  type GetLastPriceResponse (line 51) | type GetLastPriceResponse struct
  type GetCoinMarketResponse (line 82) | type GetCoinMarketResponse struct

FILE: provider/coinank/kline.go
  method Kline (line 11) | func (c *CoinankClient) Kline(ctx context.Context, symbol string, exchan...
  type KlineResult (line 53) | type KlineResult struct

FILE: provider/coinank/liquidation.go
  method LiquidationExchangeStatistics (line 11) | func (c *CoinankClient) LiquidationExchangeStatistics(ctx context.Contex...
  method LiquidationCoinAggHistory (line 30) | func (c *CoinankClient) LiquidationCoinAggHistory(ctx context.Context, b...
  method LiquidationHistory (line 53) | func (c *CoinankClient) LiquidationHistory(ctx context.Context, exchange...
  method LiquidationOrders (line 77) | func (c *CoinankClient) LiquidationOrders(ctx context.Context, baseCoin ...
  type LiquidationOrdersResponse (line 110) | type LiquidationOrdersResponse struct
  type LiquidationSymbol (line 122) | type LiquidationSymbol struct
  type LiquidationStatistic (line 132) | type LiquidationStatistic struct
  type LiquidationExchangeStatisticsResponse (line 142) | type LiquidationExchangeStatisticsResponse struct

FILE: provider/coinank/net_positions.go
  method NetPositions (line 11) | func (c *CoinankClient) NetPositions(ctx context.Context, exchange coina...
  type NetPositionsResponse (line 37) | type NetPositionsResponse struct

FILE: provider/coinank/open_interest.go
  method OpenInterestAll (line 11) | func (c *CoinankClient) OpenInterestAll(ctx context.Context, baseCoin st...
  method OpenInterestChartV2 (line 30) | func (c *CoinankClient) OpenInterestChartV2(ctx context.Context,
  method OpenInterestSymbolChart (line 58) | func (c *CoinankClient) OpenInterestSymbolChart(ctx context.Context,
  method OpenInterestKline (line 85) | func (c *CoinankClient) OpenInterestKline(ctx context.Context,
  method OpenInterestAggKline (line 112) | func (c *CoinankClient) OpenInterestAggKline(ctx context.Context,
  method TickersTopOIByEx (line 138) | func (c *CoinankClient) TickersTopOIByEx(ctx context.Context, baseCoin s...
  method InstrumentsOiVsMc (line 157) | func (c *CoinankClient) InstrumentsOiVsMc(ctx context.Context,
  type OpenInterestAllResponse (line 181) | type OpenInterestAllResponse struct
  type OpenInterestChartV2Response (line 202) | type OpenInterestChartV2Response struct
  type OpenInterestSymbolChartResponse (line 208) | type OpenInterestSymbolChartResponse struct
  type OpenInterestKlineResponse (line 226) | type OpenInterestKlineResponse struct
  type OpenInterestAggKlineResponse (line 238) | type OpenInterestAggKlineResponse struct
  type TickersTopOIByExResponse (line 246) | type TickersTopOIByExResponse struct
  type InstrumentsOiVsMcResponse (line 252) | type InstrumentsOiVsMcResponse struct

FILE: provider/hyperliquid/coins.go
  constant hyperliquidInfoURL (line 16) | hyperliquidInfoURL = "https://api.hyperliquid.xyz/info"
  constant cacheDuration (line 17) | cacheDuration      = 24 * time.Hour
  type CoinInfo (line 21) | type CoinInfo struct
  type CoinProvider (line 27) | type CoinProvider struct
    method fetchCoins (line 63) | func (p *CoinProvider) fetchCoins(ctx context.Context) error {
    method ensureUpdated (line 142) | func (p *CoinProvider) ensureUpdated(ctx context.Context) error {
    method GetAllCoins (line 154) | func (p *CoinProvider) GetAllCoins(ctx context.Context) ([]CoinInfo, e...
    method GetMainCoins (line 169) | func (p *CoinProvider) GetMainCoins(ctx context.Context, limit int) ([...
    method ForceRefresh (line 221) | func (p *CoinProvider) ForceRefresh(ctx context.Context) error {
  function GetProvider (line 41) | func GetProvider() *CoinProvider {
  type metaResponse (line 51) | type metaResponse struct
  type assetCtx (line 58) | type assetCtx struct
  function GetAllCoinSymbols (line 193) | func GetAllCoinSymbols(ctx context.Context) ([]string, error) {
  function GetMainCoinSymbols (line 207) | func GetMainCoinSymbols(ctx context.Context, limit int) ([]string, error) {

FILE: provider/hyperliquid/kline.go
  constant MainnetAPIURL (line 15) | MainnetAPIURL = "https://api.hyperliquid.xyz/info"
  constant TestnetAPIURL (line 16) | TestnetAPIURL = "https://api.hyperliquid-testnet.xyz/info"
  type Candle (line 20) | type Candle struct
  type CandleRequest (line 34) | type CandleRequest struct
  type CandleRequestBody (line 40) | type CandleRequestBody struct
  type Client (line 48) | type Client struct
    method GetCandles (line 77) | func (c *Client) GetCandles(ctx context.Context, coin string, interval...
    method GetAllMids (line 140) | func (c *Client) GetAllMids(ctx context.Context) (map[string]string, e...
    method GetAllMidsXYZ (line 145) | func (c *Client) GetAllMidsXYZ(ctx context.Context) (map[string]string...
    method GetAllMidsWithDex (line 150) | func (c *Client) GetAllMidsWithDex(ctx context.Context, dex string) (m...
    method GetMeta (line 190) | func (c *Client) GetMeta(ctx context.Context) (*Meta, error) {
  function NewClient (line 54) | func NewClient() *Client {
  function NewTestnetClient (line 64) | func NewTestnetClient() *Client {
  type Meta (line 227) | type Meta struct
  type AssetInfo (line 232) | type AssetInfo struct
  function NormalizeCoin (line 244) | func NormalizeCoin(symbol string) string {
  function MapTimeframe (line 249) | func MapTimeframe(interval string) string {
  function getIntervalDuration (line 287) | func getIntervalDuration(interval string) time.Duration {
  constant XYZDex (line 317) | XYZDex = "xyz"
  function IsStockPerp (line 357) | func IsStockPerp(symbol string) bool {
  function IsXYZAsset (line 368) | func IsXYZAsset(symbol string) bool {
  function NormalizeCoinBase (line 386) | func NormalizeCoinBase(symbol string) string {
  function FormatCoinForAPI (line 408) | func FormatCoinForAPI(symbol string) string {

FILE: provider/hyperliquid/kline_test.go
  function TestGetCandles_BTC (line 11) | func TestGetCandles_BTC(t *testing.T) {
  function TestGetCandles_TSLA (line 38) | func TestGetCandles_TSLA(t *testing.T) {
  function TestGetCandles_StockPerps (line 66) | func TestGetCandles_StockPerps(t *testing.T) {
  function TestGetAllMids (line 92) | func TestGetAllMids(t *testing.T) {
  function TestGetAllMidsXYZ (line 115) | func TestGetAllMidsXYZ(t *testing.T) {
  function TestGetMeta (line 133) | func TestGetMeta(t *testing.T) {
  function TestNormalizeCoin (line 153) | func TestNormalizeCoin(t *testing.T) {
  function TestIsStockPerp (line 175) | func TestIsStockPerp(t *testing.T) {
  function TestFormatCoinForAPI (line 197) | func TestFormatCoinForAPI(t *testing.T) {

FILE: provider/nofxos/ai500.go
  type CoinData (line 12) | type CoinData struct
  type AI500Response (line 25) | type AI500Response struct
  method GetAI500List (line 34) | func (c *Client) GetAI500List() ([]CoinData, error) {
  method fetchAI500 (line 59) | func (c *Client) fetchAI500() ([]CoinData, error) {
  method GetTopRatedCoins (line 93) | func (c *Client) GetTopRatedCoins(limit int) ([]string, error) {
  method GetAvailableCoins (line 137) | func (c *Client) GetAvailableCoins() ([]string, error) {
  function NormalizeSymbol (line 156) | func NormalizeSymbol(symbol string) string {

FILE: provider/nofxos/client.go
  constant DefaultBaseURL (line 17) | DefaultBaseURL = "https://nofxos.ai"
  constant DefaultTimeout (line 18) | DefaultTimeout = 30 * time.Second
  constant DefaultAuthKey (line 19) | DefaultAuthKey = "cm_568c67eae410d912c54c"
  type Client (line 23) | type Client struct
    method SetConfig (line 63) | func (c *Client) SetConfig(baseURL, authKey string) {
    method GetBaseURL (line 75) | func (c *Client) GetBaseURL() string {
    method GetAuthKey (line 82) | func (c *Client) GetAuthKey() string {
    method doRequest (line 89) | func (c *Client) doRequest(endpoint string) ([]byte, error) {
  function DefaultClient (line 36) | func DefaultClient() *Client {
  function NewClient (line 48) | func NewClient(baseURL, authKey string) *Client {
  type APIError (line 127) | type APIError struct
    method Error (line 132) | func (e *APIError) Error() string {
  function ExtractAuthKey (line 137) | func ExtractAuthKey(url string) string {

FILE: provider/nofxos/coin.go
  type QuantData (line 11) | type QuantData struct
  type NetflowData (line 20) | type NetflowData struct
  type FlowTypeData (line 26) | type FlowTypeData struct
  type OIData (line 32) | type OIData struct
  type OIDeltaData (line 40) | type OIDeltaData struct
  type CoinResponse (line 47) | type CoinResponse struct
  method GetCoinData (line 54) | func (c *Client) GetCoinData(symbol string, include string) (*QuantData,...
  method GetCoinDataBatch (line 87) | func (c *Client) GetCoinDataBatch(symbols []string, include string) map[...
  function FormatQuantDataForAI (line 107) | func FormatQuantDataForAI(symbol string, data *QuantData, lang Language)...
  function formatQuantDataZH (line 118) | func formatQuantDataZH(symbol string, data *QuantData) string {
  function formatQuantDataEN (line 168) | func formatQuantDataEN(symbol string, data *QuantData) string {

FILE: provider/nofxos/netflow.go
  type NetFlowPosition (line 12) | type NetFlowPosition struct
  type NetFlowResponse (line 20) | type NetFlowResponse struct
  type NetFlowRankingData (line 34) | type NetFlowRankingData struct
  method GetNetFlowRanking (line 45) | func (c *Client) GetNetFlowRanking(duration string, limit int) (*NetFlow...
  method fetchNetFlowRanking (line 98) | func (c *Client) fetchNetFlowRanking(rankType, duration string, limit in...
  function FormatNetFlowRankingForAI (line 120) | func FormatNetFlowRankingForAI(data *NetFlowRankingData, lang Language) ...
  function formatNetFlowRankingZH (line 131) | func formatNetFlowRankingZH(data *NetFlowRankingData) string {
  function formatNetFlowRankingEN (line 198) | func formatNetFlowRankingEN(data *NetFlowRankingData) string {

FILE: provider/nofxos/oi.go
  type OIPosition (line 12) | type OIPosition struct
  type OIRankingResponse (line 26) | type OIRankingResponse struct
  type OIRankingData (line 41) | type OIRankingData struct
  method GetOIRanking (line 50) | func (c *Client) GetOIRanking(duration string, limit int) (*OIRankingDat...
  method fetchOIRanking (line 86) | func (c *Client) fetchOIRanking(rankType, duration string, limit int) ([...
  method GetOITopPositions (line 108) | func (c *Client) GetOITopPositions() ([]OIPosition, error) {
  method GetOITopSymbols (line 117) | func (c *Client) GetOITopSymbols() ([]string, error) {
  method GetOILowPositions (line 133) | func (c *Client) GetOILowPositions() ([]OIPosition, error) {
  method GetOILowSymbols (line 142) | func (c *Client) GetOILowSymbols() ([]string, error) {
  function FormatOIRankingForAI (line 158) | func FormatOIRankingForAI(data *OIRankingData, lang Language) string {
  function formatOIRankingZH (line 169) | func formatOIRankingZH(data *OIRankingData) string {
  function formatOIRankingEN (line 204) | func formatOIRankingEN(data *OIRankingData) string {

FILE: provider/nofxos/price.go
  type PriceRankingItem (line 12) | type PriceRankingItem struct
  type PriceRankingDuration (line 25) | type PriceRankingDuration struct
  type PriceRankingResponse (line 31) | type PriceRankingResponse struct
  type PriceRankingData (line 41) | type PriceRankingData struct
  method GetPriceRanking (line 47) | func (c *Client) GetPriceRanking(durations string, limit int) (*PriceRan...
  function FormatPriceRankingForAI (line 87) | func FormatPriceRankingForAI(data *PriceRankingData, lang Language) stri...
  function formatPriceRankingZH (line 98) | func formatPriceRankingZH(data *PriceRankingData) string {
  function formatPriceRankingEN (line 141) | func formatPriceRankingEN(data *PriceRankingData) string {

FILE: provider/nofxos/util.go
  type Language (line 6) | type Language
  constant LangChinese (line 9) | LangChinese Language = "zh-CN"
  constant LangEnglish (line 10) | LangEnglish Language = "en-US"
  function formatValue (line 14) | func formatValue(v float64) string {

FILE: provider/twelvedata/kline.go
  constant BaseURL (line 16) | BaseURL = "https://api.twelvedata.com"
  type Bar (line 20) | type Bar struct
  type TimeSeriesResponse (line 30) | type TimeSeriesResponse struct
  type Meta (line 39) | type Meta struct
  type QuoteResponse (line 50) | type QuoteResponse struct
  type Client (line 72) | type Client struct
    method GetTimeSeries (line 99) | func (c *Client) GetTimeSeries(ctx context.Context, symbol string, int...
    method GetQuote (line 148) | func (c *Client) GetQuote(ctx context.Context, symbol string) (*QuoteR...
  function NewClient (line 78) | func NewClient() *Client {
  function NewClientWithKey (line 88) | func NewClientWithKey(apiKey string) *Client {
  function MapTimeframe (line 195) | func MapTimeframe(interval string) string {
  function ParseBar (line 235) | func ParseBar(bar Bar) (open, high, low, close, volume float64, timestam...

FILE: security/url_validator.go
  function init (line 17) | func init() {
  type SSRFError (line 43) | type SSRFError struct
    method Error (line 48) | func (e *SSRFError) Error() string {
  function isPrivateIP (line 53) | func isPrivateIP(ip net.IP) bool {
  function ValidateURL (line 85) | func ValidateURL(rawURL string) error {
  function SafeHTTPClient (line 158) | func SafeHTTPClient(timeout time.Duration) *http.Client {
  function SafeGet (line 208) | func SafeGet(rawURL string, timeout time.Duration) (*http.Response, erro...

FILE: store/ai_model.go
  type AIModelStore (line 15) | type AIModelStore struct
    method initTables (line 40) | func (s *AIModelStore) initTables() error {
    method initDefaultData (line 52) | func (s *AIModelStore) initDefaultData() error {
    method List (line 58) | func (s *AIModelStore) List(userID string) ([]*AIModel, error) {
    method Get (line 68) | func (s *AIModelStore) Get(userID, modelID string) (*AIModel, error) {
    method GetByID (line 98) | func (s *AIModelStore) GetByID(modelID string) (*AIModel, error) {
    method GetDefault (line 112) | func (s *AIModelStore) GetDefault(userID string) (*AIModel, error) {
    method firstEnabled (line 129) | func (s *AIModelStore) firstEnabled(userID string) (*AIModel, error) {
    method GetAnyEnabled (line 142) | func (s *AIModelStore) GetAnyEnabled() (*AIModel, error) {
    method Update (line 155) | func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey,...
    method Create (line 238) | func (s *AIModelStore) Create(userID, id, name, provider string, enabl...
  type AIModel (line 20) | type AIModel struct
    method TableName (line 33) | func (AIModel) TableName() string { return "ai_models" }
  function NewAIModelStore (line 36) | func NewAIModelStore(db *gorm.DB) *AIModelStore {

FILE: store/decision.go
  type DecisionStore (line 12) | type DecisionStore struct
    method initTables (line 113) | func (s *DecisionStore) initTables() error {
    method LogDecision (line 148) | func (s *DecisionStore) LogDecision(record *DecisionRecord) error {
    method GetLatestRecords (line 185) | func (s *DecisionStore) GetLatestRecords(traderID string, n int) ([]*D...
    method GetAllLatestRecords (line 209) | func (s *DecisionStore) GetAllLatestRecords(n int) ([]*DecisionRecord,...
    method GetRecordsByDate (line 230) | func (s *DecisionStore) GetRecordsByDate(traderID string, date time.Ti...
    method CleanOldRecords (line 250) | func (s *DecisionStore) CleanOldRecords(traderID string, days int) (in...
    method GetStatistics (line 262) | func (s *DecisionStore) GetStatistics(traderID string) (*Statistics, e...
    method GetAllStatistics (line 281) | func (s *DecisionStore) GetAllStatistics() (*Statistics, error) {
    method GetLastCycleNumber (line 300) | func (s *DecisionStore) GetLastCycleNumber(traderID string) (int, erro...
  type DecisionRecordDB (line 17) | type DecisionRecordDB struct
    method TableName (line 36) | func (DecisionRecordDB) TableName() string { return "decision_records" }
    method toRecord (line 126) | func (db *DecisionRecordDB) toRecord() *DecisionRecord {
  type DecisionRecord (line 39) | type DecisionRecord struct
  type AccountSnapshot (line 60) | type AccountSnapshot struct
  type PositionSnapshot (line 70) | type PositionSnapshot struct
  type DecisionAction (line 82) | type DecisionAction struct
  type Statistics (line 99) | type Statistics struct
  function NewDecisionStore (line 108) | func NewDecisionStore(db *gorm.DB) *DecisionStore {

FILE: store/driver.go
  type DBType (line 15) | type DBType
  constant DBTypeSQLite (line 18) | DBTypeSQLite   DBType = "sqlite"
  constant DBTypePostgres (line 19) | DBTypePostgres DBType = "postgres"
  type DBConfig (line 23) | type DBConfig struct
  type DBDriver (line 35) | type DBDriver struct
    method DB (line 94) | func (d *DBDriver) DB() *sql.DB {
    method Close (line 99) | func (d *DBDriver) Close() error {
    method AutoIncrement (line 104) | func (d *DBDriver) AutoIncrement() string {
    method Placeholder (line 115) | func (d *DBDriver) Placeholder(index int) string {
    method ConvertPlaceholders (line 125) | func (d *DBDriver) ConvertPlaceholders(query string) string {
    method TableExists (line 141) | func (d *DBDriver) TableExists(tableName string) (bool, error) {
    method UpsertSyntax (line 167) | func (d *DBDriver) UpsertSyntax() string {
  function NewDBDriver (line 41) | func NewDBDriver(cfg DBConfig) (*DBDriver, error) {
  function NewDBDriverFromEnv (line 65) | func NewDBDriverFromEnv() (*DBDriver, error) {
  function openSQLite (line 172) | func openSQLite(path string) (*sql.DB, error) {
  function openPostgres (line 210) | func openPostgres(cfg DBConfig) (*sql.DB, error) {
  function getEnv (line 235) | func getEnv(key, defaultValue string) string {
  function convertQuery (line 244) | func convertQuery(query string, dbType DBType) string {
  function boolDefault (line 270) | func boolDefault(dbType DBType, value bool) string {

FILE: store/equity.go
  type EquityStore (line 11) | type EquityStore struct
    method initTables (line 36) | func (s *EquityStore) initTables() error {
    method Save (line 49) | func (s *EquityStore) Save(snapshot *EquitySnapshot) error {
    method GetLatest (line 65) | func (s *EquityStore) GetLatest(traderID string, limit int) ([]*Equity...
    method GetByTimeRange (line 84) | func (s *EquityStore) GetByTimeRange(traderID string, start, end time....
    method GetAllTradersLatest (line 96) | func (s *EquityStore) GetAllTradersLatest() (map[string]*EquitySnapsho...
    method CleanOldRecords (line 121) | func (s *EquityStore) CleanOldRecords(traderID string, days int) (int6...
    method GetCount (line 133) | func (s *EquityStore) GetCount(traderID string) (int, error) {
    method MigrateFromDecision (line 140) | func (s *EquityStore) MigrateFromDecision() (int64, error) {
  type EquitySnapshot (line 16) | type EquitySnapshot struct
    method TableName (line 28) | func (EquitySnapshot) TableName() string { return "trader_equity_snaps...
  function NewEquityStore (line 31) | func NewEquityStore(db *gorm.DB) *EquityStore {

FILE: store/exchange.go
  type ExchangeStore (line 14) | type ExchangeStore struct
    method initTables (line 51) | func (s *ExchangeStore) initTables() error {
    method migrateToMultiAccount (line 80) | func (s *ExchangeStore) migrateToMultiAccount() error {
    method initDefaultData (line 135) | func (s *ExchangeStore) initDefaultData() error {
    method List (line 141) | func (s *ExchangeStore) List(userID string) ([]*Exchange, error) {
    method GetByID (line 151) | func (s *ExchangeStore) GetByID(userID, id string) (*Exchange, error) {
    method Create (line 185) | func (s *ExchangeStore) Create(userID, exchangeType, accountName strin...
    method Update (line 231) | func (s *ExchangeStore) Update(userID, id string, enabled bool, apiKey...
    method UpdateAccountName (line 280) | func (s *ExchangeStore) UpdateAccountName(userID, id, accountName stri...
    method Delete (line 297) | func (s *ExchangeStore) Delete(userID, id string) error {
    method CreateLegacy (line 311) | func (s *ExchangeStore) CreateLegacy(userID, id, name, typ string, ena...
  type Exchange (line 19) | type Exchange struct
    method TableName (line 44) | func (Exchange) TableName() string { return "exchanges" }
  function NewExchangeStore (line 47) | func NewExchangeStore(db *gorm.DB) *ExchangeStore {
  function getExchangeNameAndType (line 161) | func getExchangeNameAndType(exchangeType string) (name string, typ strin...

FILE: store/gorm.go
  function DB (line 17) | func DB() *gorm.DB {
  function InitGorm (line 22) | func InitGorm(dbPath string) (*gorm.DB, error) {
  function InitGormPostgres (line 53) | func InitGormPostgres(host string, port int, user, password, dbname, ssl...
  function InitGormWithConfig (line 84) | func InitGormWithConfig(cfg DBConfig) (*gorm.DB, error) {
  function ForUser (line 109) | func ForUser(userID string) func(*gorm.DB) *gorm.DB {
  function ForTrader (line 116) | func ForTrader(traderID string) func(*gorm.DB) *gorm.DB {
  function OpenPositions (line 123) | func OpenPositions() func(*gorm.DB) *gorm.DB {
  function ClosedPositions (line 130) | func ClosedPositions() func(*gorm.DB) *gorm.DB {
  function OrderByCreatedDesc (line 137) | func OrderByCreatedDesc() func(*gorm.DB) *gorm.DB {
  function OrderByUpdatedDesc (line 144) | func OrderByUpdatedDesc() func(*gorm.DB) *gorm.DB {
  function Paginate (line 151) | func Paginate(limit, offset int) func(*gorm.DB) *gorm.DB {

FILE: store/grid.go
  type GridConfigModel (line 15) | type GridConfigModel struct
    method TableName (line 72) | func (GridConfigModel) TableName() string {
  type GridInstanceModel (line 77) | type GridInstanceModel struct
    method TableName (line 132) | func (GridInstanceModel) TableName() string {
  type GridLevelModel (line 137) | type GridLevelModel struct
    method TableName (line 156) | func (GridLevelModel) TableName() string {
  type GridEventModel (line 161) | type GridEventModel struct
    method TableName (line 179) | func (GridEventModel) TableName() string {
  type GridRegimeAssessmentModel (line 184) | type GridRegimeAssessmentModel struct
    method TableName (line 205) | func (GridRegimeAssessmentModel) TableName() string {
  type GridStore (line 212) | type GridStore struct
    method InitTables (line 222) | func (s *GridStore) InitTables() error {
    method SaveGridConfig (line 258) | func (s *GridStore) SaveGridConfig(config *GridConfigModel) error {
    method LoadGridConfig (line 267) | func (s *GridStore) LoadGridConfig(id string) (*GridConfigModel, error) {
    method LoadGridConfigByTrader (line 277) | func (s *GridStore) LoadGridConfigByTrader(traderID string) (*GridConf...
    method ListGridConfigs (line 287) | func (s *GridStore) ListGridConfigs(userID string) ([]GridConfigModel,...
    method DeleteGridConfig (line 297) | func (s *GridStore) DeleteGridConfig(id string) error {
    method SaveGridInstance (line 331) | func (s *GridStore) SaveGridInstance(instance *GridInstanceModel) error {
    method LoadGridInstance (line 337) | func (s *GridStore) LoadGridInstance(configID string) (*GridInstanceMo...
    method LoadGridInstanceByID (line 349) | func (s *GridStore) LoadGridInstanceByID(id string) (*GridInstanceMode...
    method ListGridInstances (line 359) | func (s *GridStore) ListGridInstances(configID string) ([]GridInstance...
    method SaveGridLevel (line 373) | func (s *GridStore) SaveGridLevel(level *GridLevelModel) error {
    method SaveGridLevels (line 379) | func (s *GridStore) SaveGridLevels(levels []GridLevelModel) error {
    method LoadGridLevels (line 391) | func (s *GridStore) LoadGridLevels(instanceID string) ([]GridLevelMode...
    method DeleteGridLevels (line 403) | func (s *GridStore) DeleteGridLevels(instanceID string) error {
    method SaveGridEvent (line 410) | func (s *GridStore) SaveGridEvent(event *GridEventModel) error {
    method LoadRecentGridEvents (line 418) | func (s *GridStore) LoadRecentGridEvents(instanceID string, limit int)...
    method LoadGridEventsByType (line 433) | func (s *GridStore) LoadGridEventsByType(instanceID, eventType string,...
    method CountGridEvents (line 448) | func (s *GridStore) CountGridEvents(instanceID string) (int64, error) {
    method SaveGridRegimeAssessment (line 459) | func (s *GridStore) SaveGridRegimeAssessment(assessment *GridRegimeAss...
    method LoadLatestGridRegime (line 467) | func (s *GridStore) LoadLatestGridRegime(instanceID string) (*GridRegi...
    method LoadGridRegimeHistory (line 479) | func (s *GridStore) LoadGridRegimeHistory(instanceID string, limit int...
    method GetGridInstanceStatistics (line 496) | func (s *GridStore) GetGridInstanceStatistics(instanceID string) (map[...
    method GetGridPerformanceMetrics (line 551) | func (s *GridStore) GetGridPerformanceMetrics(instanceID string, from,...
  function NewGridStore (line 217) | func NewGridStore(db *gorm.DB) *GridStore {

FILE: store/order.go
  type TraderOrder (line 13) | type TraderOrder struct
    method TableName (line 46) | func (TraderOrder) TableName() string {
  type TraderFill (line 52) | type TraderFill struct
    method TableName (line 73) | func (TraderFill) TableName() string {
  type OrderStore (line 78) | type OrderStore struct
    method InitTables (line 88) | func (s *OrderStore) InitTables() error {
    method CreateOrder (line 152) | func (s *OrderStore) CreateOrder(order *TraderOrder) error {
    method UpdateOrderStatus (line 169) | func (s *OrderStore) UpdateOrderStatus(id int64, status string, filled...
    method CreateFill (line 186) | func (s *OrderStore) CreateFill(fill *TraderFill) error {
    method GetFillByExchangeTradeID (line 202) | func (s *OrderStore) GetFillByExchangeTradeID(exchangeID, exchangeTrad...
    method GetOrderByExchangeID (line 215) | func (s *OrderStore) GetOrderByExchangeID(exchangeID, exchangeOrderID ...
    method GetTraderOrders (line 228) | func (s *OrderStore) GetTraderOrders(traderID string, limit int) ([]*T...
    method GetTraderOrdersFiltered (line 241) | func (s *OrderStore) GetTraderOrdersFiltered(traderID string, symbol s...
    method GetOrderFills (line 262) | func (s *OrderStore) GetOrderFills(orderID int64) ([]*TraderFill, erro...
    method GetTraderOrderStats (line 274) | func (s *OrderStore) GetTraderOrderStats(traderID string) (map[string]...
    method CleanupDuplicateOrders (line 306) | func (s *OrderStore) CleanupDuplicateOrders() (int, error) {
    method CleanupDuplicateFills (line 322) | func (s *OrderStore) CleanupDuplicateFills() (int, error) {
    method GetDuplicateOrdersCount (line 338) | func (s *OrderStore) GetDuplicateOrdersCount() (int, error) {
    method GetDuplicateFillsCount (line 353) | func (s *OrderStore) GetDuplicateFillsCount() (int, error) {
    method GetMaxTradeIDsByExchange (line 367) | func (s *OrderStore) GetMaxTradeIDsByExchange(exchangeID string) (map[...
    method GetLastFillTimeByExchange (line 401) | func (s *OrderStore) GetLastFillTimeByExchange(exchangeID string) (int...
    method GetRecentFillSymbolsByExchange (line 413) | func (s *OrderStore) GetRecentFillSymbolsByExchange(exchangeID string,...
  function NewOrderStore (line 83) | func NewOrderStore(db *gorm.DB) *OrderStore {

FILE: store/position.go
  function adaptivePriceRound (line 16) | func adaptivePriceRound(price float64, referencePrices ...float64) float...
  function getPriceDecimalPlaces (line 51) | func getPriceDecimalPlaces(price float64) int {
  function formatDuration (line 64) | func formatDuration(d time.Duration) string {
  function formatDurationMs (line 69) | func formatDurationMs(ms int64) string {
  type TraderPosition (line 97) | type TraderPosition struct
    method TableName (line 124) | func (TraderPosition) TableName() string {
  type PositionStore (line 129) | type PositionStore struct
    method isPostgres (line 139) | func (s *PositionStore) isPostgres() bool {
    method InitTables (line 144) | func (s *PositionStore) InitTables() error {
    method Create (line 189) | func (s *PositionStore) Create(pos *TraderPosition) error {
    method ClosePosition (line 198) | func (s *PositionStore) ClosePosition(id int64, exitPrice float64, exi...
    method UpdatePositionQuantityAndPrice (line 213) | func (s *PositionStore) UpdatePositionQuantityAndPrice(id int64, addQt...
    method ReducePositionQuantity (line 243) | func (s *PositionStore) ReducePositionQuantity(id int64, reduceQty flo...
    method UpdatePositionExchangeInfo (line 291) | func (s *PositionStore) UpdatePositionExchangeInfo(id int64, exchangeI...
    method ClosePositionFully (line 302) | func (s *PositionStore) ClosePositionFully(id int64, exitPrice float64...
    method DeleteAllOpenPositions (line 327) | func (s *PositionStore) DeleteAllOpenPositions(traderID string) error {
    method GetOpenPositions (line 332) | func (s *PositionStore) GetOpenPositions(traderID string) ([]*TraderPo...
    method GetOpenPositionBySymbol (line 351) | func (s *PositionStore) GetOpenPositionBySymbol(traderID, symbol, side...
    method GetClosedPositions (line 384) | func (s *PositionStore) GetClosedPositions(traderID string, limit int)...
    method GetAllOpenPositions (line 403) | func (s *PositionStore) GetAllOpenPositions() ([]*TraderPosition, erro...
    method ExistsWithExchangePositionID (line 421) | func (s *PositionStore) ExistsWithExchangePositionID(exchangeID, excha...
    method GetOpenPositionByExchangePositionID (line 437) | func (s *PositionStore) GetOpenPositionByExchangePositionID(exchangeID...
    method CreateOpenPosition (line 459) | func (s *PositionStore) CreateOpenPosition(pos *TraderPosition) error {
    method ClosePositionWithAccurateData (line 507) | func (s *PositionStore) ClosePositionWithAccurateData(id int64, exitPr...
  function NewPositionStore (line 134) | func NewPositionStore(db *gorm.DB) *PositionStore {

FILE: store/position_builder.go
  type PositionBuilder (line 16) | type PositionBuilder struct
    method ProcessTrade (line 29) | func (pb *PositionBuilder) ProcessTrade(
    method handleOpen (line 45) | func (pb *PositionBuilder) handleOpen(
    method handleClose (line 97) | func (pb *PositionBuilder) handleClose(
  function NewPositionBuilder (line 21) | func NewPositionBuilder(positionStore *PositionStore) *PositionBuilder {
  function quantitiesMatch (line 178) | func quantitiesMatch(a, b float64) bool {

FILE: store/position_history.go
  type HistorySummary (line 12) | type HistorySummary struct
  method GetHistorySummary (line 38) | func (s *PositionStore) GetHistorySummary(traderID string) (*HistorySumm...
  method calculateStreaks (line 120) | func (s *PositionStore) calculateStreaks(traderID string, summary *Histo...
  type ClosedPnLRecord (line 176) | type ClosedPnLRecord struct
  method CreateFromClosedPnL (line 193) | func (s *PositionStore) CreateFromClosedPnL(traderID, exchangeID, exchan...
  method GetLastClosedPositionTime (line 276) | func (s *PositionStore) GetLastClosedPositionTime(traderID string) (int6...
  method SyncClosedPositions (line 293) | func (s *PositionStore) SyncClosedPositions(traderID, exchangeID, exchan...

FILE: store/position_query.go
  type TraderStats (line 10) | type TraderStats struct
  method GetPositionStats (line 25) | func (s *PositionStore) GetPositionStats(traderID string) (map[string]in...
  method GetFullStats (line 58) | func (s *PositionStore) GetFullStats(traderID string) (*TraderStats, err...
  type RecentTrade (line 118) | type RecentTrade struct
  method GetRecentTrades (line 131) | func (s *PositionStore) GetRecentTrades(traderID string, limit int) ([]R...
  function calculateSharpeRatioFromPnls (line 173) | func calculateSharpeRatioFromPnls(pnls []float64) float64 {
  function calculateMaxDrawdownFromPnls (line 198) | func calculateMaxDrawdownFromPnls(pnls []float64) float64 {
  type SymbolStats (line 225) | type SymbolStats struct
  method GetSymbolStats (line 236) | func (s *PositionStore) GetSymbolStats(traderID string, limit int) ([]Sy...
  type HoldingTimeStats (line 298) | type HoldingTimeStats struct
  method GetHoldingTimeStats (line 306) | func (s *PositionStore) GetHoldingTimeStats(traderID string) ([]HoldingT...
  type DirectionStats (line 367) | type DirectionStats struct
  method GetDirectionStats (line 376) | func (s *PositionStore) GetDirectionStats(traderID string) ([]DirectionS...

FILE: store/store.go
  type Store (line 15) | type Store struct
    method initTables (line 118) | func (s *Store) initTables() error {
    method initDefaultData (line 167) | func (s *Store) initDefaultData() error {
    method User (line 187) | func (s *Store) User() *UserStore {
    method AIModel (line 197) | func (s *Store) AIModel() *AIModelStore {
    method Exchange (line 207) | func (s *Store) Exchange() *ExchangeStore {
    method Trader (line 217) | func (s *Store) Trader() *TraderStore {
    method Decision (line 227) | func (s *Store) Decision() *DecisionStore {
    method Position (line 237) | func (s *Store) Position() *PositionStore {
    method Strategy (line 247) | func (s *Store) Strategy() *StrategyStore {
    method Equity (line 257) | func (s *Store) Equity() *EquityStore {
    method Order (line 267) | func (s *Store) Order() *OrderStore {
    method Grid (line 277) | func (s *Store) Grid() *GridStore {
    method TelegramConfig (line 287) | func (s *Store) TelegramConfig() TelegramConfigStore {
    method Close (line 297) | func (s *Store) Close() error {
    method GormDB (line 308) | func (s *Store) GormDB() *gorm.DB {
    method Driver (line 313) | func (s *Store) Driver() *DBDriver {
    method DBType (line 318) | func (s *Store) DBType() DBType {
    method q (line 335) | func (s *Store) q(query string) string {
    method DB (line 341) | func (s *Store) DB() *sql.DB {
    method GetSystemConfig (line 346) | func (s *Store) GetSystemConfig(key string) (string, error) {
    method SetSystemConfig (line 362) | func (s *Store) SetSystemConfig(key, value string) error {
    method Transaction (line 371) | func (s *Store) Transaction(fn func(tx *gorm.DB) error) error {
    method TransactionSQL (line 377) | func (s *Store) TransactionSQL(fn func(tx *sql.Tx) error) error {
  function New (line 37) | func New(dbPath string) (*Store, error) {
  function NewWithConfig (line 68) | func NewWithConfig(cfg DBConfig) (*Store, error) {
  function NewFromGorm (line 103) | func NewFromGorm(gdb *gorm.DB) (*Store, error) {
  function NewFromDB (line 113) | func NewFromDB(db *sql.DB) *Store {

FILE: store/strategy.go
  type StrategyStore (line 12) | type StrategyStore struct
    method initTables (line 240) | func (s *StrategyStore) initTables() error {
    method initDefaultData (line 245) | func (s *StrategyStore) initDefaultData() error {
    method Create (line 369) | func (s *StrategyStore) Create(strategy *Strategy) error {
    method Update (line 374) | func (s *StrategyStore) Update(strategy *Strategy) error {
    method Delete (line 388) | func (s *StrategyStore) Delete(userID, id string) error {
    method List (line 399) | func (s *StrategyStore) List(userID string) ([]*Strategy, error) {
    method ListPublic (line 411) | func (s *StrategyStore) ListPublic() ([]*Strategy, error) {
    method Get (line 423) | func (s *StrategyStore) Get(userID, id string) (*Strategy, error) {
    method GetActive (line 434) | func (s *StrategyStore) GetActive(userID string) (*Strategy, error) {
    method GetDefault (line 448) | func (s *StrategyStore) GetDefault() (*Strategy, error) {
    method SetActive (line 458) | func (s *StrategyStore) SetActive(userID, strategyID string) error {
    method Duplicate (line 474) | func (s *StrategyStore) Duplicate(userID, sourceID, newID, newName str...
  type Strategy (line 17) | type Strategy struct
    method TableName (line 31) | func (Strategy) TableName() string { return "strategies" }
    method ParseConfig (line 496) | func (s *Strategy) ParseConfig() (*StrategyConfig, error) {
    method SetConfig (line 505) | func (s *Strategy) SetConfig(config *StrategyConfig) error {
  type StrategyConfig (line 34) | type StrategyConfig struct
  type GridStrategyConfig (line 57) | type GridStrategyConfig struct
  type PromptSectionsConfig (line 91) | type PromptSectionsConfig struct
  type CoinSourceConfig (line 103) | type CoinSourceConfig struct
  type IndicatorConfig (line 132) | type IndicatorConfig struct
  type KlineConfig (line 183) | type KlineConfig struct
  type ExternalDataSource (line 199) | type ExternalDataSource struct
  type RiskControlConfig (line 210) | type RiskControlConfig struct
  function NewStrategyStore (line 236) | func NewStrategyStore(db *gorm.DB) *StrategyStore {
  function GetDefaultStrategyConfig (line 251) | func GetDefaultStrategyConfig(lang string) StrategyConfig {

FILE: store/telegram_config.go
  type TelegramConfig (line 13) | type TelegramConfig struct
    method String (line 26) | func (tc TelegramConfig) String() string {
  type TelegramConfigStore (line 36) | type TelegramConfigStore interface
  type telegramConfigStore (line 48) | type telegramConfigStore struct
    method initTables (line 58) | func (s *telegramConfigStore) initTables() error {
    method Get (line 62) | func (s *telegramConfigStore) Get() (*TelegramConfig, error) {
    method SaveToken (line 72) | func (s *telegramConfigStore) SaveToken(botToken string) error {
    method Save (line 76) | func (s *telegramConfigStore) Save(botToken, modelID string) error {
    method BindUser (line 90) | func (s *telegramConfigStore) BindUser(chatID int64, username string) ...
    method IsBound (line 105) | func (s *telegramConfigStore) IsBound() (bool, error) {
    method GetBoundChatID (line 118) | func (s *telegramConfigStore) GetBoundChatID() (int64, error) {
    method Unbind (line 131) | func (s *telegramConfigStore) Unbind() error {
    method SetLanguage (line 140) | func (s *telegramConfigStore) SetLanguage(lang string) error {
    method GetLanguage (line 153) | func (s *telegramConfigStore) GetLanguage() string {
  function NewTelegramConfigStore (line 54) | func NewTelegramConfigStore(db *gorm.DB) TelegramConfigStore {

FILE: store/trader.go
  type TraderStore (line 11) | type TraderStore struct
    method initTables (line 60) | func (s *TraderStore) initTables() error {
    method Create (line 77) | func (s *TraderStore) Create(trader *Trader) error {
    method List (line 82) | func (s *TraderStore) List(userID string) ([]*Trader, error) {
    method UpdateStatus (line 94) | func (s *TraderStore) UpdateStatus(userID, id string, isRunning bool) ...
    method UpdateShowInCompetition (line 101) | func (s *TraderStore) UpdateShowInCompetition(userID, id string, showI...
    method Update (line 108) | func (s *TraderStore) Update(trader *Trader) error {
    method UpdateInitialBalance (line 138) | func (s *TraderStore) UpdateInitialBalance(userID, id string, newBalan...
    method UpdateCustomPrompt (line 145) | func (s *TraderStore) UpdateCustomPrompt(userID, id string, customProm...
    method Delete (line 155) | func (s *TraderStore) Delete(userID, id string) error {
    method GetFullConfig (line 164) | func (s *TraderStore) GetFullConfig(userID, traderID string) (*TraderF...
    method getStrategyByID (line 204) | func (s *TraderStore) getStrategyByID(userID, strategyID string) (*Str...
    method getActiveOrDefaultStrategy (line 215) | func (s *TraderStore) getActiveOrDefaultStrategy(userID string) (*Stra...
    method GetByID (line 233) | func (s *TraderStore) GetByID(traderID string) (*Trader, error) {
    method ListAll (line 243) | func (s *TraderStore) ListAll() ([]*Trader, error) {
    method ListByExchangeID (line 253) | func (s *TraderStore) ListByExchangeID(userID, exchangeID string) ([]*...
    method ListByAIModelID (line 263) | func (s *TraderStore) ListByAIModelID(userID, aiModelID string) ([]*Tr...
  function NewTraderStore (line 16) | func NewTraderStore(db *gorm.DB) *TraderStore {
  type Trader (line 21) | type Trader struct
    method TableName (line 48) | func (Trader) TableName() string {
  type TraderFullConfig (line 53) | type TraderFullConfig struct

FILE: store/user.go
  type UserStore (line 10) | type UserStore struct
    method initTables (line 30) | func (s *UserStore) initTables() error {
    method Create (line 62) | func (s *UserStore) Create(user *User) error {
    method GetByEmail (line 67) | func (s *UserStore) GetByEmail(email string) (*User, error) {
    method GetByID (line 77) | func (s *UserStore) GetByID(userID string) (*User, error) {
    method Count (line 87) | func (s *UserStore) Count() (int, error) {
    method GetAllIDs (line 94) | func (s *UserStore) GetAllIDs() ([]string, error) {
    method GetAll (line 101) | func (s *UserStore) GetAll() ([]User, error) {
    method UpdatePassword (line 108) | func (s *UserStore) UpdatePassword(userID, passwordHash string) error {
    method EnsureAdmin (line 116) | func (s *UserStore) EnsureAdmin() error {
  type User (line 15) | type User struct
    method TableName (line 23) | func (User) TableName() string { return "users" }
  function NewUserStore (line 26) | func NewUserStore(db *gorm.DB) *UserStore {

FILE: telegram/agent/agent.go
  constant maxIterations (line 13) | maxIterations = 10
  type Agent (line 48) | type Agent struct
    method buildAccountContext (line 77) | func (a *Agent) buildAccountContext() string {
    method Run (line 194) | func (a *Agent) Run(userMessage string, onChunk func(string)) string {
    method ResetMemory (line 283) | func (a *Agent) ResetMemory() {
  function New (line 57) | func New(apiPort int, botToken, userID string, getLLM func() mcp.AIClien...
  function GenerateBotToken (line 70) | func GenerateBotToken(userID string) (string, error) {

FILE: telegram/agent/agent_test.go
  type mockLLM (line 17) | type mockLLM struct
    method SetAPIKey (line 23) | func (m *mockLLM) SetAPIKey(_, _, _ string)   {}
    method SetTimeout (line 24) | func (m *mockLLM) SetTimeout(_ time.Duration) {}
    method CallWithMessages (line 26) | func (m *mockLLM) CallWithMessages(_, _ string) (string, error) { retu...
    method CallWithRequest (line 28) | func (m *mockLLM) CallWithRequest(req *mcp.Request) (string, error) {
    method CallWithRequestStream (line 36) | func (m *mockLLM) CallWithRequestStream(req *mcp.Request, onChunk func...
    method CallWithRequestFull (line 47) | func (m *mockLLM) CallWithRequestFull(req *mcp.Request) (*mcp.LLMRespo...
    method next (line 52) | func (m *mockLLM) next() (*mcp.LLMResponse, error) {
  function toolCall (line 62) | func toolCall(id, method, path string, body string) *mcp.LLMResponse {
  function textReply (line 79) | func textReply(content string) *mcp.LLMResponse {
  function mockGetLLM (line 83) | func mockGetLLM(llm *mockLLM) func() mcp.AIClient {
  constant testPrompt (line 87) | testPrompt = "You are a test assistant."
  function mockAPIServer (line 90) | func mockAPIServer(handlers map[string]string) (*httptest.Server, int) {
  function TestAgentDirectReply (line 113) | func TestAgentDirectReply(t *testing.T) {
  function TestAgentAPICall (line 128) | func TestAgentAPICall(t *testing.T) {
  function TestAgentMultiStep (line 151) | func TestAgentMultiStep(t *testing.T) {
  function TestAgentAPIResultInContext (line 176) | func TestAgentAPIResultInContext(t *testing.T) {
  function TestNarrationStructurallyImpossible (line 208) | func TestNarrationStructurallyImpossible(t *testing.T) {
  function TestOnChunkCalledWithFinalReply (line 243) | func TestOnChunkCalledWithFinalReply(t *testing.T) {
  function TestCreateStrategyWorkflow (line 277) | func TestCreateStrategyWorkflow(t *testing.T) {
  function TestFullSetupWorkflow (line 302) | func TestFullSetupWorkflow(t *testing.T) {
  function TestStartExistingTrader (line 352) | func TestStartExistingTrader(t *testing.T) {
  function TestMaxIterations (line 389) | func TestMaxIterations(t *testing.T) {
  function TestToolCallIDPropagated (line 415) | func TestToolCallIDPropagated(t *testing.T) {

FILE: telegram/agent/apicall.go
  type apiCallTool (line 16) | type apiCallTool struct
    method execute (line 38) | func (t *apiCallTool) execute(req *apiRequest) string {
  type apiRequest (line 23) | type apiRequest struct
  function newAPICallTool (line 29) | func newAPICallTool(port int, token string) *apiCallTool {

FILE: telegram/agent/manager.go
  type Manager (line 12) | type Manager struct
    method Run (line 42) | func (m *Manager) Run(chatID int64, userMessage string, onChunk func(s...
    method Reset (line 55) | func (m *Manager) Reset(chatID int64) {
    method getOrCreate (line 64) | func (m *Manager) getOrCreate(chatID int64) (*Agent, chan struct{}) {
  function NewManager (line 26) | func NewManager(apiPort int, botToken, userEmail, userID string, getLLM ...

FILE: telegram/agent/prompt.go
  function BuildAgentPrompt (line 9) | func BuildAgentPrompt(apiDocs, userEmail, userID string) string {

FILE: telegram/bot.go
  function Start (line 23) | func Start(cfg *config.Config, st *store.Store, reloadCh <-chan struct{}) {
  function resolveToken (line 45) | func resolveToken(cfg *config.Config, st *store.Store) string {
  function runBot (line 54) | func runBot(token string, cfg *config.Config, st *store.Store) bool {
  function sendMsg (line 254) | func sendMsg(bot *tgbotapi.BotAPI, chatID int64, text string) {
  function sendMarkdownMsg (line 259) | func sendMarkdownMsg(bot *tgbotapi.BotAPI, chatID int64, text string) {
  function newLLMClient (line 270) | func newLLMClient(st *store.Store, userID string) mcp.AIClient {
  function isUSDCProvider (line 319) | func isUSDCProvider(provider string) bool {
  function clientForProvider (line 323) | func clientForProvider(provider string) mcp.AIClient {
  function statusMsg (line 335) | func statusMsg(st *store.Store, userID string, apiPort int, lang string)...
  function langMenuMsg (line 401) | func langMenuMsg() string {
  function parseLangChoice (line 405) | func parseLangChoice(text string) string {
  function helpMsg (line 417) | func helpMsg(lang string) string {

FILE: telegram/session/memory.go
  constant compactionThresholdTokens (line 10) | compactionThresholdTokens = 3000
  constant charsPerToken (line 11) | charsPerToken             = 3
  type Message (line 14) | type Message struct
  type Memory (line 22) | type Memory struct
    method Add (line 33) | func (m *Memory) Add(role, content string) {
    method BuildContext (line 41) | func (m *Memory) BuildContext() string {
    method Reset (line 58) | func (m *Memory) Reset() {
    method ResetFull (line 63) | func (m *Memory) ResetFull() {
    method estimateTokens (line 68) | func (m *Memory) estimateTokens() int {
    method compact (line 79) | func (m *Memory) compact() {
  function NewMemory (line 28) | func NewMemory(llm mcp.AIClient) *Memory {

FILE: telemetry/experience.go
  constant telemetryEndpoint (line 13) | telemetryEndpoint = "https://www.google-analytics.com/mp/collect"
  constant tid (line 14) | tid               = "G-14J8SY6F0J"
  constant tk (line 15) | tk                = "sgPLmshGTPiF-X57rzEIKA"
  type Client (line 24) | type Client struct
  type TradeEvent (line 30) | type TradeEvent struct
  type AIUsageEvent (line 40) | type AIUsageEvent struct
  type telemetryPayload (line 50) | type telemetryPayload struct
  type telemetryEvent (line 55) | type telemetryEvent struct
  function Init (line 60) | func Init(enabled bool, installationID string) {
  function SetInstallationID (line 69) | func SetInstallationID(id string) {
  function GetInstallationID (line 78) | func GetInstallationID() string {
  function SetEnabled (line 87) | func SetEnabled(enabled bool) {
  function IsEnabled (line 96) | func IsEnabled() bool {
  function TrackTrade (line 105) | func TrackTrade(event TradeEvent) {
  function sendTradeEvent (line 117) | func sendTradeEvent(event TradeEvent) error {
  function TrackStartup (line 163) | func TrackStartup(version string) {
  function TrackAIUsage (line 200) | func TrackAIUsage(event AIUsageEvent) {

FILE: trader/aster/trader.go
  type AsterTrader (line 27) | type AsterTrader struct
    method genNonce (line 83) | func (t *AsterTrader) genNonce() uint64 {
    method getPrecision (line 88) | func (t *AsterTrader) getPrecision(symbol string) (SymbolPrecision, er...
    method formatPrice (line 165) | func (t *AsterTrader) formatPrice(symbol string, price float64) (float...
    method formatQuantity (line 182) | func (t *AsterTrader) formatQuantity(symbol string, quantity float64) ...
    method formatFloatWithPrecision (line 199) | func (t *AsterTrader) formatFloatWithPrecision(value float64, precisio...
    method normalizeAndStringify (line 211) | func (t *AsterTrader) normalizeAndStringify(params map[string]interfac...
    method normalize (line 224) | func (t *AsterTrader) normalize(v interface{}) (interface{}, error) {
    method sign (line 268) | func (t *AsterTrader) sign(params map[string]interface{}, nonce uint64...
    method request (line 329) | func (t *AsterTrader) request(method, endpoint string, params map[stri...
    method doRequest (line 372) | func (t *AsterTrader) doRequest(method, endpoint string, params map[st...
  type SymbolPrecision (line 41) | type SymbolPrecision struct
  function NewAsterTrader (line 52) | func NewAsterTrader(user, signer, privateKeyHex string) (*AsterTrader, e...
  function roundToTickSize (line 152) | func roundToTickSize(value float64, tickSize float64) float64 {

FILE: trader/aster/trader_account.go
  method GetBalance (line 16) | func (t *AsterTrader) GetBalance() (map[string]interface{}, error) {
  method GetMarketPrice (line 104) | func (t *AsterTrader) GetMarketPrice(symbol string) (float64, error) {
  method GetClosedPnL (line 133) | func (t *AsterTrader) GetClosedPnL(startTime time.Time, limit int) ([]ty...
  type AsterTradeRecord (line 188) | type AsterTradeRecord struct
  method GetTrades (line 204) | func (t *AsterTrader) GetTrades(startTime time.Time, limit int) ([]types...
  method GetOrderBook (line 254) | func (t *AsterTrader) GetOrderBook(symbol string, depth int) (bids, asks...

FILE: trader/aster/trader_orders.go
  method OpenLong (line 13) | func (t *AsterTrader) OpenLong(symbol string, quantity float64, leverage...
  method OpenShort (line 86) | func (t *AsterTrader) OpenShort(symbol string, quantity float64, leverag...
  method CloseLong (line 159) | func (t *AsterTrader) CloseLong(symbol string, quantity float64) (map[st...
  method CloseShort (line 241) | func (t *AsterTrader) CloseShort(symbol string, quantity float64) (map[s...
  method SetStopLoss (line 324) | func (t *AsterTrader) SetStopLoss(symbol string, positionSide string, qu...
  method SetTakeProfit (line 365) | func (t *AsterTrader) SetTakeProfit(symbol string, positionSide string, ...
  method CancelStopLossOrders (line 406) | func (t *AsterTrader) CancelStopLossOrders(symbol string) error {
  method CancelTakeProfitOrders (line 465) | func (t *AsterTrader) CancelTakeProfitOrders(symbol string) error {
  method CancelAllOrders (line 524) | func (t *AsterTrader) CancelAllOrders(symbol string) error {
  method CancelStopOrders (line 534) | func (t *AsterTrader) CancelStopOrders(symbol string) error {
  method FormatQuantity (line 589) | func (t *AsterTrader) FormatQuantity(symbol string, quantity float64) (s...
  method GetOrderStatus (line 598) | func (t *AsterTrader) GetOrderStatus(symbol string, orderID string) (map...
  method GetOpenOrders (line 647) | func (t *AsterTrader) GetOpenOrders(symbol string) ([]types.OpenOrder, e...
  method PlaceLimitOrder (line 697) | func (t *AsterTrader) PlaceLimitOrder(req *types.LimitOrderRequest) (*ty...
  method CancelOrder (line 775) | func (t *AsterTrader) CancelOrder(symbol, orderID string) error {

FILE: trader/aster/trader_positions.go
  method GetPositions (line 12) | func (t *AsterTrader) GetPositions() ([]map[string]interface{}, error) {
  method SetMarginMode (line 66) | func (t *AsterTrader) SetMarginMode(symbol string, isCrossMargin bool) e...
  method SetLeverage (line 113) | func (t *AsterTrader) SetLeverage(symbol string, leverage int) error {

FILE: trader/aster/trader_sync.go
  method SyncOrdersFromAster (line 17) | func (t *AsterTrader) SyncOrdersFromAster(traderID string, exchangeID st...
  function deriveAsterOrderAction (line 149) | func deriveAsterOrderAction(side, positionSide string, realizedPnL float...
  method StartOrderSync (line 183) | func (t *AsterTrader) StartOrderSync(traderID string, exchangeID string,...

FILE: trader/aster/trader_test.go
  type AsterTraderTestSuite (line 23) | type AsterTraderTestSuite struct
    method Cleanup (line 216) | func (s *AsterTraderTestSuite) Cleanup() {
  function NewAsterTraderTestSuite (line 29) | func NewAsterTraderTestSuite(t *testing.T) *AsterTraderTestSuite {
  function TestAsterTrader_InterfaceCompliance (line 228) | func TestAsterTrader_InterfaceCompliance(t *testing.T) {
  function TestAsterTrader_CommonInterface (line 233) | func TestAsterTrader_CommonInterface(t *testing.T) {
  function TestNewAsterTrader (line 247) | func TestNewAsterTrader(t *testing.T) {

FILE: trader/auto_trader.go
  type AutoTraderConfig (line 26) | type AutoTraderConfig struct
  type AutoTrader (line 117) | type AutoTrader struct
    method Run (line 362) | func (at *AutoTrader) Run() error {
    method Stop (line 506) | func (at *AutoTrader) Stop() {
    method GetID (line 521) | func (at *AutoTrader) GetID() string {
    method GetUnderlyingTrader (line 527) | func (at *AutoTrader) GetUnderlyingTrader() Trader {
    method GetName (line 532) | func (at *AutoTrader) GetName() string {
    method GetAIModel (line 537) | func (at *AutoTrader) GetAIModel() string {
    method GetExchange (line 542) | func (at *AutoTrader) GetExchange() string {
    method GetShowInCompetition (line 547) | func (at *AutoTrader) GetShowInCompetition() bool {
    method SetShowInCompetition (line 552) | func (at *AutoTrader) SetShowInCompetition(show bool) {
    method SetCustomPrompt (line 557) | func (at *AutoTrader) SetCustomPrompt(prompt string) {
    method SetOverrideBasePrompt (line 562) | func (at *AutoTrader) SetOverrideBasePrompt(override bool) {
    method GetSystemPromptTemplate (line 567) | func (at *AutoTrader) GetSystemPromptTemplate() string {
    method GetStore (line 578) | func (at *AutoTrader) GetStore() *store.Store {
  function NewAutoTrader (line 152) | func NewAutoTrader(config AutoTraderConfig, st *store.Store, userID stri...
  function calculatePnLPercentage (line 584) | func calculatePnLPercentage(unrealizedPnl, marginUsed float64) float64 {

FILE: trader/auto_trader_decision.go
  method saveEquitySnapshot (line 15) | func (at *AutoTrader) saveEquitySnapshot(ctx *kernel.Context) {
  method saveDecision (line 36) | func (at *AutoTrader) saveDecision(record *store.DecisionRecord) error {
  method GetStatus (line 59) | func (at *AutoTrader) GetStatus() map[string]interface{} {
  method GetAccountInfo (line 97) | func (at *AutoTrader) GetAccountInfo() (map[string]interface{}, error) {
  method GetPositions (line 194) | func (at *AutoTrader) GetPositions() ([]map[string]interface{}, error) {
  method recordAndConfirmOrder (line 244) | func (at *AutoTrader) recordAndConfirmOrder(orderResult map[string]inter...
  method recordPositionChange (line 360) | func (at *AutoTrader) recordPositionChange(orderID, symbol, side, action...
  method createOrderRecord (line 410) | func (at *AutoTrader) createOrderRecord(orderID, symbol, action, positio...
  method recordOrderFill (line 459) | func (at *AutoTrader) recordOrderFill(orderRecordID int64, exchangeOrder...
  method GetOpenOrders (line 525) | func (at *AutoTrader) GetOpenOrders(symbol string) ([]OpenOrder, error) {

FILE: trader/auto_trader_grid.go
  type GridState (line 19) | type GridState struct
  function NewGridState (line 75) | func NewGridState(config *store.GridStrategyConfig) *GridState {
  type BreakoutType (line 89) | type BreakoutType
  constant BreakoutNone (line 92) | BreakoutNone  BreakoutType = "none"
  constant BreakoutUpper (line 93) | BreakoutUpper BreakoutType = "upper"
  constant BreakoutLower (line 94) | BreakoutLower BreakoutType = "lower"
  method checkBreakout (line 99) | func (at *AutoTrader) checkBreakout() (BreakoutType, float64) {
  method checkMaxDrawdown (line 133) | func (at *AutoTrader) checkMaxDrawdown() (bool, float64) {
  method checkDailyLossLimit (line 185) | func (at *AutoTrader) checkDailyLossLimit() (bool, float64) {
  method updateDailyPnL (line 212) | func (at *AutoTrader) updateDailyPnL(realizedPnL float64) {
  method emergencyExit (line 220) | func (at *AutoTrader) emergencyExit(reason string) error {
  method handleBreakout (line 255) | func (at *AutoTrader) handleBreakout(breakoutType BreakoutType, breakout...
  method InitializeGrid (line 289) | func (at *AutoTrader) InitializeGrid() error {
  method RunGridCycle (line 342) | func (at *AutoTrader) RunGridCycle() error {
  method buildGridContext (line 454) | func (at *AutoTrader) buildGridContext() (*kernel.GridContext, error) {
  method executeGridDecision (line 519) | func (at *AutoTrader) executeGridDecision(d *kernel.Decision) error {
  method IsGridStrategy (line 552) | func (at *AutoTrader) IsGridStrategy() bool {
  method saveGridDecisionRecord (line 560) | func (at *AutoTrader) saveGridDecisionRecord(decision *kernel.FullDecisi...
  type GridRiskInfo (line 610) | type GridRiskInfo struct

FILE: trader/auto_trader_grid_levels.go
  method calculateDefaultBounds (line 16) | func (at *AutoTrader) calculateDefaultBounds(price float64, config *stor...
  method calculateATRBounds (line 24) | func (at *AutoTrader) calculateATRBounds(price float64, mktData *market....
  method initializeGridLevels (line 46) | func (at *AutoTrader) initializeGridLevels(currentPrice float64, config ...
  method applyGridDirection (line 98) | func (at *AutoTrader) applyGridDirection(currentPrice float64) {
  method checkGridSkew (line 186) | func (at *AutoTrader) checkGridSkew() (bool, int, int) {
  method autoAdjustGrid (line 228) | func (at *AutoTrader) autoAdjustGrid() {
  method calculateDefaultBoundsLocked (line 333) | func (at *AutoTrader) calculateDefaultBoundsLocked(price float64, config...
  method calculateATRBoundsLocked (line 341) | func (at *AutoTrader) calculateATRBoundsLocked(price float64, mktData *m...
  method initializeGridLevelsLocked (line 363) | func (at *AutoTrader) initializeGridLevelsLocked(currentPrice float64, c...
  method applyGridDirectionLocked (line 414) | func (at *AutoTrader) applyGridDirectionLocked(currentPrice float64) {

FILE: trader/auto_trader_grid_orders.go
  method checkTotalPositionLimit (line 17) | func (at *AutoTrader) checkTotalPositionLimit(symbol string, additionalV...
  method placeGridLimitOrder (line 58) | func (at *AutoTrader) placeGridLimitOrder(d *kernel.Decision, side strin...
  method cancelGridOrder (line 154) | func (at *AutoTrader) cancelGridOrder(d *kernel.Decision) error {
  method cancelAllGridOrders (line 181) | func (at *AutoTrader) cancelAllGridOrders() error {
  method pauseGrid (line 205) | func (at *AutoTrader) pauseGrid(reason string) error {
  method resumeGrid (line 217) | func (at *AutoTrader) resumeGrid() error {
  method adjustGrid (line 227) | func (at *AutoTrader) adjustGrid(d *kernel.Decision) error {
  method syncGridState (line 247) | func (at *AutoTrader) syncGridState() {
  method closeAllPositions (line 324) | func (at *AutoTrader) closeAllPositions() error {
  method checkAndExecuteStopLoss (line 360) | func (at *AutoTrader) checkAndExecuteStopLoss() {

FILE: trader/auto_trader_grid_regime.go
  method checkBoxBreakout (line 16) | func (at *AutoTrader) checkBoxBreakout() error {
  method executeBreakoutAction (line 79) | func (at *AutoTrader) executeBreakoutAction(action BreakoutAction) error {
  method executeDirectionAdjustment (line 119) | func (at *AutoTrader) executeDirectionAdjustment(newDirection market.Gri...
  method adjustGridDirection (line 140) | func (at *AutoTrader) adjustGridDirection(newDirection market.GridDirect...
  method checkFalseBreakoutRecovery (line 169) | func (at *AutoTrader) checkFalseBreakoutRecovery() error {
  method GetGridRiskInfo (line 227) | func (at *AutoTrader) GetGridRiskInfo() *GridRiskInfo {

FILE: trader/auto_trader_loop.go
  method runCycle (line 14) | func (at *AutoTrader) runCycle() error {
  method buildTradingContext (line 232) | func (at *AutoTrader) buildTradingContext() (*kernel.Context, error) {
  function sortDecisionsByPriority (line 527) | func sortDecisionsByPriority(decisions []kernel.Decision) []kernel.Decis...

FILE: trader/auto_trader_orders.go
  method executeDecisionWithRecord (line 13) | func (at *AutoTrader) executeDecisionWithRecord(decision *kernel.Decisio...
  method executeOpenLongWithRecord (line 32) | func (at *AutoTrader) executeOpenLongWithRecord(decision *kernel.Decisio...
  method executeOpenShortWithRecord (line 149) | func (at *AutoTrader) executeOpenShortWithRecord(decision *kernel.Decisi...
  method executeCloseLongWithRecord (line 266) | func (at *AutoTrader) executeCloseLongWithRecord(decision *kernel.Decisi...
  method executeCloseShortWithRecord (line 330) | func (at *AutoTrader) executeCloseShortWithRecord(decision *kernel.Decis...

FILE: trader/auto_trader_risk.go
  method startDrawdownMonitor (line 11) | func (at *AutoTrader) startDrawdownMonitor() {
  method checkPositionDrawdown (line 34) | func (at *AutoTrader) checkPositionDrawdown() {
  method emergencyClosePosition (line 110) | func (at *AutoTrader) emergencyClosePosition(symbol, side string) error {
  method GetPeakPnLCache (line 132) | func (at *AutoTrader) GetPeakPnLCache() map[string]float64 {
  method UpdatePeakPnL (line 145) | func (at *AutoTrader) UpdatePeakPnL(symbol, side string, currentPnLPct f...
  method ClearPeakPnLCache (line 162) | func (at *AutoTrader) ClearPeakPnLCache(symbol, side string) {
  function isBTCETH (line 175) | func isBTCETH(symbol string) bool {
  method enforcePositionValueRatio (line 185) | func (at *AutoTrader) enforcePositionValueRatio(positionSizeUSD float64,...
  method enforceMinPositionSize (line 220) | func (at *AutoTrader) enforceMinPositionSize(positionSizeUSD float64) er...
  method enforceMaxPositions (line 237) | func (at *AutoTrader) enforceMaxPositions(currentPositionCount int) error {
  function getSideFromAction (line 254) | func getSideFromAction(action string) string {

FILE: trader/binance/futures.go
  function getBrOrderID (line 21) | func getBrOrderID() string {
  type FuturesTrader (line 46) | type FuturesTrader struct
    method setDualSidePosition (line 89) | func (t *FuturesTrader) setDualSidePosition() error {
  function NewFuturesTrader (line 64) | func NewFuturesTrader(apiKey, secretKey string, userId string) *FuturesT...
  function syncBinanceServerTime (line 111) | func syncBinanceServerTime(client *futures.Client) {
  function contains (line 126) | func contains(s, substr string) bool {
  function stringContains (line 130) | func stringContains(s, substr string) bool {
  function calculatePrecision (line 140) | func calculatePrecision(stepSize string) int {
  function trimTrailingZeros (line 163) | func trimTrailingZeros(s string) string {

FILE: trader/binance/futures_account.go
  method GetBalance (line 13) | func (t *FuturesTrader) GetBalance() (map[string]interface{}, error) {
  method GetClosedPnL (line 55) | func (t *FuturesTrader) GetClosedPnL(startTime time.Time, limit int) ([]...
  method GetTrades (line 112) | func (t *FuturesTrader) GetTrades(startTime time.Time, limit int) ([]typ...
  method GetTradesForSymbol (line 155) | func (t *FuturesTrader) GetTradesForSymbol(symbol string, startTime time...
  method GetTradesForSymbolFromID (line 198) | func (t *FuturesTrader) GetTradesForSymbolFromID(symbol string, fromID i...
  method GetCommissionSymbols (line 241) | func (t *FuturesTrader) GetCommissionSymbols(lastSyncTime time.Time) ([]...
  method GetPnLSymbols (line 268) | func (t *FuturesTrader) GetPnLSymbols(lastSyncTime time.Time) ([]string,...

FILE: trader/binance/futures_orders.go
  method OpenLong (line 14) | func (t *FuturesTrader) OpenLong(symbol string, quantity float64, levera...
  method OpenShort (line 69) | func (t *FuturesTrader) OpenShort(symbol string, quantity float64, lever...
  method CloseLong (line 124) | func (t *FuturesTrader) CloseLong(symbol string, quantity float64) (map[...
  method CloseShort (line 179) | func (t *FuturesTrader) CloseShort(symbol string, quantity float64) (map...
  method CancelStopLossOrders (line 235) | func (t *FuturesTrader) CancelStopLossOrders(symbol string) error {
  method CancelTakeProfitOrders (line 311) | func (t *FuturesTrader) CancelTakeProfitOrders(symbol string) error {
  method CancelAllOrders (line 387) | func (t *FuturesTrader) CancelAllOrders(symbol string) error {
  method PlaceLimitOrder (line 418) | func (t *FuturesTrader) PlaceLimitOrder(req *types.LimitOrderRequest) (*...
  method CancelOrder (line 484) | func (t *FuturesTrader) CancelOrder(symbol, orderID string) error {
  method GetOrderBook (line 506) | func (t *FuturesTrader) GetOrderBook(symbol string, depth int) (bids, as...
  method CancelStopOrders (line 537) | func (t *FuturesTrader) CancelStopOrders(symbol string) error {
  method GetOpenOrders (line 596) | func (t *FuturesTrader) GetOpenOrders(symbol string) ([]types.OpenOrder,...
  method SetStopLoss (line 655) | func (t *FuturesTrader) SetStopLoss(symbol string, positionSide string, ...
  method SetTakeProfit (line 689) | func (t *FuturesTrader) SetTakeProfit(symbol string, positionSide string...
  method GetOrderStatus (line 722) | func (t *FuturesTrader) GetOrderStatus(symbol string, orderID string) (m...

FILE: trader/binance/futures_positions.go
  method GetPositions (line 14) | func (t *FuturesTrader) GetPositions() ([]map[string]interface{}, error) {
  method SetMarginMode (line 69) | func (t *FuturesTrader) SetMarginMode(symbol string, isCrossMargin bool)...
  method SetLeverage (line 120) | func (t *FuturesTrader) SetLeverage(symbol string, leverage int) error {
  method GetMarketPrice (line 166) | func (t *FuturesTrader) GetMarketPrice(symbol string) (float64, error) {
  method CalculatePositionSize (line 185) | func (t *FuturesTrader) CalculatePositionSize(balance, riskPercent, pric...
  method GetMinNotional (line 193) | func (t *FuturesTrader) GetMinNotional(symbol string) float64 {
  method CheckMinNotional (line 199) | func (t *FuturesTrader) CheckMinNotional(symbol string, quantity float64...
  method GetSymbolPrecision (line 219) | func (t *FuturesTrader) GetSymbolPrecision(symbol string) (int, error) {
  method FormatQuantity (line 244) | func (t *FuturesTrader) FormatQuantity(symbol string, quantity float64) ...
  method GetSymbolPricePrecision (line 256) | func (t *FuturesTrader) GetSymbolPricePrecision(symbol string) (int, err...
  method FormatPrice (line 280) | func (t *FuturesTrader) FormatPrice(symbol string, price float64) (strin...

FILE: trader/binance/futures_test.go
  type BinanceFuturesTestSuite (line 24) | type BinanceFuturesTestSuite struct
    method Cleanup (line 290) | func (s *BinanceFuturesTestSuite) Cleanup() {
  function NewBinanceFuturesTestSuite (line 30) | func NewBinanceFuturesTestSuite(t *testing.T) *BinanceFuturesTestSuite {
  function TestFuturesTrader_InterfaceCompliance (line 302) | func TestFuturesTrader_InterfaceCompliance(t *testing.T) {
  function TestFuturesTrader_CommonInterface (line 307) | func TestFuturesTrader_CommonInterface(t *testing.T) {
  function TestNewFuturesTrader (line 321) | func TestNewFuturesTrader(t *testing.T) {
  function TestCalculatePositionSize (line 360) | func TestCalculatePositionSize(t *testing.T) {
  function TestGetBrOrderID (line 406) | func TestGetBrOrderID(t *testing.T) {

FILE: trader/binance/order_sync.go
  method SyncOrdersFromBinance (line 24) | func (t *FuturesTrader) SyncOrdersFromBinance(traderID string, exchangeI...
  method getPositionSymbols (line 295) | func (t *FuturesTrader) getPositionSymbols() []string {
  method determineOrderAction (line 311) | func (t *FuturesTrader) determineOrderAction(side, positionSide string, ...
  method StartOrderSync (line 352) | func (t *FuturesTrader) StartOrderSync(traderID string, exchangeID strin...

FILE: trader/binance/order_sync_test.go
  function skipIfNoLiveTest (line 11) | func skipIfNoLiveTest(t *testing.T) {
  function getBinanceTestCredentials (line 17) | func getBinanceTestCredentials(t *testing.T) (string, string) {
  function createBinanceTestTrader (line 26) | func createBinanceTestTrader(t *testing.T) *FuturesTrader {
  function TestBinanceConnection (line 33) | func TestBinanceConnection(t *testing.T) {
  function TestBinanceGetPositions (line 45) | func TestBinanceGetPositions(t *testing.T) {
  function TestBinanceGetCommissionSymbols (line 68) | func TestBinanceGetCommissionSymbols(t *testing.T) {
  function TestBinanceGetPnLSymbols (line 95) | func TestBinanceGetPnLSymbols(t *testing.T) {
  function TestBinanceGetAllIncomeTypes (line 121) | func TestBinanceGetAllIncomeTypes(t *testing.T) {
  function TestBinanceGetTradesForSymbol (line 177) | func TestBinanceGetTradesForSymbol(t *testing.T) {
  function TestBinanceTimestampFormats (line 216) | func TestBinanceTimestampFormats(t *testing.T) {
  function TestBinanceFullSyncSimulation (line 250) | func TestBinanceFullSyncSimulation(t *testing.T) {
  function TestBinanceTradeIDRange (line 359) | func TestBinanceTradeIDRange(t *testing.T) {
  function TestBinanceIncomeAPIDirectCall (line 411) | func TestBinanceIncomeAPIDirectCall(t *testing.T) {

FILE: trader/binance/sync_e2e_test.go
  function TestBinanceSyncE2E (line 11) | func TestBinanceSyncE2E(t *testing.T) {
  function TestBinanceSyncWithExistingData (line 143) | func TestBinanceSyncWithExistingData(t *testing.T) {

FILE: trader/binance/sync_verify_test.go
  function repeatStr (line 14) | func repeatStr(s string, n int) string {
  function TestBinanceSyncVerification (line 19) | func TestBinanceSyncVerification(t *testing.T) {
  function floatEqual (line 410) | func floatEqual(a, b, tolerance float64) bool {
  function TestBinanceDetailedTradeComparison (line 415) | func TestBinanceDetailedTradeComparison(t *testing.T) {

FILE: trader/bitget/order_sync.go
  type BitgetTrade (line 16) | type BitgetTrade struct
  method GetTrades (line 32) | func (t *BitgetTrader) GetTrades(startTime time.Time, limit int) ([]Bitg...
  method SyncOrdersFromBitget (line 159) | func (t *BitgetTrader) SyncOrdersFromBitget(traderID string, exchangeID ...
  method StartOrderSync (line 282) | func (t *BitgetTrader) StartOrderSync(traderID string, exchangeID string...

FILE: trader/bitget/trader.go
  constant bitgetBaseURL (line 21) | bitgetBaseURL          = "https://api.bitget.com"
  constant bitgetAccountPath (line 22) | bitgetAccountPath      = "/api/v2/mix/account/accounts"
  constant bitgetPositionPath (line 23) | bitgetPositionPath     = "/api/v2/mix/position/all-position"
  constant bitgetOrderPath (line 24) | bitgetOrderPath        = "/api/v2/mix/order/place-order"
  constant bitgetLeveragePath (line 25) | bitgetLeveragePath     = "/api/v2/mix/account/set-leverage"
  constant bitgetTickerPath (line 26) | bitgetTickerPath       = "/api/v2/mix/market/ticker"
  constant bitgetContractsPath (line 27) | bitgetContractsPath    = "/api/v2/mix/market/contracts"
  constant bitgetCancelOrderPath (line 28) | bitgetCancelOrderPath  = "/api/v2/mix/order/cancel-order"
  constant bitgetPendingPath (line 29) | bitgetPendingPath      = "/api/v2/mix/order/orders-pending"
  constant bitgetHistoryPath (line 30) | bitgetHistoryPath      = "/api/v2/mix/order/orders-history"
  constant bitgetMarginModePath (line 31) | bitgetMarginModePath   = "/api/v2/mix/account/set-margin-mode"
  constant bitgetPositionModePath (line 32) | bitgetPositionModePath = "/api/v2/mix/account/set-position-mode"
  type BitgetTrader (line 36) | type BitgetTrader struct
    method setPositionMode (line 110) | func (t *BitgetTrader) setPositionMode() error {
    method sign (line 129) | func (t *BitgetTrader) sign(timestamp, method, requestPath, body strin...
    method doRequest (line 138) | func (t *BitgetTrader) doRequest(method, path string, body interface{}...
    method convertSymbol (line 215) | func (t *BitgetTrader) convertSymbol(symbol string) string {
    method getContract (line 221) | func (t *BitgetTrader) getContract(symbol string) (*BitgetContract, er...
    method FormatQuantity (line 291) | func (t *BitgetTrader) FormatQuantity(symbol string, quantity float64)...
    method clearCache (line 303) | func (t *BitgetTrader) clearCache() {
  type BitgetContract (line 64) | type BitgetContract struct
  type BitgetResponse (line 76) | type BitgetResponse struct
  function NewBitgetTrader (line 84) | func NewBitgetTrader(apiKey, secretKey, passphrase string) *BitgetTrader {
  function genBitgetClientOid (line 314) | func genBitgetClientOid() string {

FILE: trader/bitget/trader_account.go
  method GetBalance (line 13) | func (t *BitgetTrader) GetBalance() (map[string]interface{}, error) {
  method SetMarginMode (line 71) | func (t *BitgetTrader) SetMarginMode(symbol string, isCrossMargin bool) ...
  method SetLeverage (line 103) | func (t *BitgetTrader) SetLeverage(symbol string, leverage int) error {
  method GetMarketPrice (line 127) | func (t *BitgetTrader) GetMarketPrice(symbol string) (float64, error) {
  method GetOrderBook (line 162) | func (t *BitgetTrader) GetOrderBook(symbol string, depth int) (bids, ask...

FILE: trader/bitget/trader_orders.go
  method OpenLong (line 13) | func (t *BitgetTrader) OpenLong(symbol string, quantity float64, leverag...
  method OpenShort (line 67) | func (t *BitgetTrader) OpenShort(symbol string, quantity float64, levera...
  method CloseLong (line 121) | func (t *BitgetTrader) CloseLong(symbol string, quantity float64) (map[s...
  method CloseShort (line 184) | func (t *BitgetTrader) CloseShort(symbol string, quantity float64) (map[...
  method SetStopLoss (line 252) | func (t *BitgetTrader) SetStopLoss(symbol string, positionSide string, q...
  method SetTakeProfit (line 291) | func (t *BitgetTrader) SetTakeProfit(symbol string, positionSide string,...
  method CancelStopLossOrders (line 330) | func (t *BitgetTrader) CancelStopLossOrders(symbol string) error {
  method CancelTakeProfitOrders (line 335) | func (t *BitgetTrader) CancelTakeProfitOrders(symbol string) error {
  method cancelPlanOrders (line 340) | func (t *BitgetTrader) cancelPlanOrders(symbol string, planType string) ...
  method CancelAllOrders (line 380) | func (t *BitgetTrader) CancelAllOrders(symbol string) error {
  method CancelStopOrders (line 423) | func (t *BitgetTrader) CancelStopOrders(symbol string) error {
  method GetOrderStatus (line 430) | func (t *BitgetTrader) GetOrderStatus(symbol string, orderID string) (ma...
  method GetOpenOrders (line 494) | func (t *BitgetTrader) GetOpenOrders(symbol string) ([]types.OpenOrder, ...
  method PlaceLimitOrder (line 625) | func (t *BitgetTrader) PlaceLimitOrder(req *types.LimitOrderRequest) (*t...
  method CancelOrder (line 695) | func (t *BitgetTrader) CancelOrder(symbol, orderID string) error {

FILE: trader/bitget/trader_positions.go
  method GetPositions (line 12) | func (t *BitgetTrader) GetPositions() ([]map[string]interface{}, error) {
  method GetClosedPnL (line 96) | func (t *BitgetTrader) GetClosedPnL(startTime time.Time, limit int) ([]t...

FILE: trader/bybit/order_sync.go
  type BybitTrade (line 21) | type BybitTrade struct
  method GetTrades (line 38) | func (t *BybitTrader) GetTrades(startTime time.Time, limit int) ([]Bybit...
  method getTradesViaHTTP (line 43) | func (t *BybitTrader) getTradesViaHTTP(startTime time.Time, limit int) (...
  method parseTradesResult (line 106) | func (t *BybitTrader) parseTradesResult(list []map[string]interface{}) (...
  method SyncOrdersFromBybit (line 178) | func (t *BybitTrader) SyncOrdersFromBybit(traderID string, exchangeID st...
  method StartOrderSync (line 301) | func (t *BybitTrader) StartOrderSync(traderID string, exchangeID string,...

FILE: trader/bybit/trader.go
  type BybitTrader (line 19) | type BybitTrader struct
    method getQtyStep (line 86) | func (t *BybitTrader) getQtyStep(symbol string) float64 {
    method FormatQuantity (line 144) | func (t *BybitTrader) FormatQuantity(symbol string, quantity float64) ...
    method clearCache (line 169) | func (t *BybitTrader) clearCache() {
    method parseOrderResult (line 179) | func (t *BybitTrader) parseOrderResult(result *bybit.ServerResponse) (...
  function NewBybitTrader (line 43) | func NewBybitTrader(apiKey, secretKey string) *BybitTrader {
  type headerRoundTripper (line 75) | type headerRoundTripper struct
    method RoundTrip (line 80) | func (h *headerRoundTripper) RoundTrip(req *http.Request) (*http.Respo...

FILE: trader/bybit/trader_account.go
  method GetBalance (line 18) | func (t *BybitTrader) GetBalance() (map[string]interface{}, error) {
  method GetClosedPnL (line 93) | func (t *BybitTrader) GetClosedPnL(startTime time.Time, limit int) ([]ty...
  method getClosedPnLViaHTTP (line 99) | func (t *BybitTrader) getClosedPnLViaHTTP(startTime time.Time, limit int...
  method parseClosedPnLResult (line 160) | func (t *BybitTrader) parseClosedPnLResult(resultData interface{}) ([]ty...

FILE: trader/bybit/trader_orders.go
  method OpenLong (line 16) | func (t *BybitTrader) OpenLong(symbol string, quantity float64, leverage...
  method OpenShort (line 59) | func (t *BybitTrader) OpenShort(symbol string, quantity float64, leverag...
  method CloseLong (line 102) | func (t *BybitTrader) CloseLong(symbol string, quantity float64) (map[st...
  method CloseShort (line 147) | func (t *BybitTrader) CloseShort(symbol string, quantity float64) (map[s...
  method SetLeverage (line 192) | func (t *BybitTrader) SetLeverage(symbol string, leverage int) error {
  method SetMarginMode (line 217) | func (t *BybitTrader) SetMarginMode(symbol string, isCrossMargin bool) e...
  method GetMarketPrice (line 245) | func (t *BybitTrader) GetMarketPrice(symbol string) (float64, error) {
  method SetStopLoss (line 282) | func (t *BybitTrader) SetStopLoss(symbol string, positionSide string, qu...
  method SetTakeProfit (line 328) | func (t *BybitTrader) SetTakeProfit(symbol string, positionSide string, ...
  method CancelStopLossOrders (line 374) | func (t *BybitTrader) CancelStopLossOrders(symbol string) error {
  method CancelTakeProfitOrders (line 379) | func (t *BybitTrader) CancelTakeProfitOrders(symbol string) error {
  method CancelAllOrders (line 384) | func (t *BybitTrader) CancelAllOrders(symbol string) error {
  method CancelStopOrders (line 399) | func (t *BybitTrader) CancelStopOrders(symbol string) error {
  method cancelConditionalOrders (line 409) | func (t *BybitTrader) cancelConditionalOrders(symbol string, orderType s...
  method GetOrderStatus (line 466) | func (t *BybitTrader) GetOrderStatus(symbol string, orderID string) (map...
  method GetOpenOrders (line 527) | func (t *BybitTrader) GetOpenOrders(symbol string) ([]types.OpenOrder, e...
  method PlaceLimitOrder (line 589) | func (t *BybitTrader) PlaceLimitOrder(req *types.LimitOrderRequest) (*ty...
  method CancelOrder (line 664) | func (t *BybitTrader) CancelOrder(symbol, orderID string) error {
  method GetOrderBook (line 686) | func (t *BybitTrader) GetOrderBook(symbol string, depth int) (bids, asks...

FILE: trader/bybit/trader_positions.go
  method GetPositions (line 13) | func (t *BybitTrader) GetPositions() ([]map[string]interface{}, error) {

FILE: trader/exchange_sync_test.go
  type TestScenario (line 14) | type TestScenario struct
  type TestTrade (line 21) | type TestTrade struct
  type ExpectedPosition (line 32) | type ExpectedPosition struct
  function getStandardTestScenarios (line 40) | func getStandardTestScenarios() []TestScenario {
  function runStandardTests (line 114) | func runStandardTests(t *testing.T, exchangeName string) {
  function TestAllExchangesStandardScenarios (line 192) | func TestAllExchangesStandardScenarios(t *testing.T) {
  function TestPositionAccumulationBug (line 203) | func TestPositionAccumulationBug(t *testing.T) {
  function TestQuantityPrecision (line 288) | func TestQuantityPrecision(t *testing.T) {

FILE: trader/gate/order_sync.go
  type GateTrade (line 18) | type GateTrade struct
  method GetTrades (line 34) | func (t *GateTrader) GetTrades(startTime time.Time, limit int) ([]GateTr...
  method SyncOrdersFromGate (line 153) | func (t *GateTrader) SyncOrdersFromGate(traderID string, exchangeID stri...
  method StartOrderSync (line 294) | func (t *GateTrader) StartOrderSync(traderID string, exchangeID string, ...

FILE: trader/gate/trader.go
  type GateTrader (line 15) | type GateTrader struct
    method convertSymbol (line 58) | func (t *GateTrader) convertSymbol(symbol string) string {
    method revertSymbol (line 72) | func (t *GateTrader) revertSymbol(symbol string) string {
    method getContract (line 77) | func (t *GateTrader) getContract(symbol string) (*gateapi.Contract, er...
    method clearCache (line 103) | func (t *GateTrader) clearCache() {
  function NewGateTrader (line 34) | func NewGateTrader(apiKey, secretKey string) *GateTrader {

FILE: trader/gate/trader_account.go
  method GetBalance (line 14) | func (t *GateTrader) GetBalance() (map[string]interface{}, error) {
  method GetPositions (line 50) | func (t *GateTrader) GetPositions() ([]map[string]interface{}, error) {
  method GetClosedPnL (line 126) | func (t *GateTrader) GetClosedPnL(startTime time.Time, limit int) ([]typ...

FILE: trader/gate/trader_orders.go
  method SetLeverage (line 16) | func (t *GateTrader) SetLeverage(symbol string, leverage int) error {
  method SetMarginMode (line 34) | func (t *GateTrader) SetMarginMode(symbol string, isCrossMargin bool) er...
  method OpenLong (line 43) | func (t *GateTrader) OpenLong(symbol string, quantity float64, leverage ...
  method OpenShort (line 101) | func (t *GateTrader) OpenShort(symbol string, quantity float64, leverage...
  method CloseLong (line 158) | func (t *GateTrader) CloseLong(symbol string, quantity float64) (map[str...
  method CloseShort (line 226) | func (t *GateTrader) CloseShort(symbol string, quantity float64) (map[st...
  method GetMarketPrice (line 299) | func (t *GateTrader) GetMarketPrice(symbol string) (float64, error) {
  method SetStopLoss (line 320) | func (t *GateTrader) SetStopLoss(symbol string, positionSide string, qua...
  method SetTakeProfit (line 372) | func (t *GateTrader) SetTakeProfit(symbol string, positionSide string, q...
  method CancelStopLossOrders (line 423) | func (t *GateTrader) CancelStopLossOrders(symbol string) error {
  method CancelTakeProfitOrders (line 428) | func (t *GateTrader) CancelTakeProfitOrders(symbol string) error {
  method cancelTriggerOrders (line 433) | func (t *GateTrader) cancelTriggerOrders(symbol string, orderType string...
  method CancelAllOrders (line 458) | func (t *GateTrader) CancelAllOrders(symbol string) error {
  method CancelStopOrders (line 477) | func (t *GateTrader) CancelStopOrders(symbol string) error {
  method FormatQuantity (line 484) | func (t *GateTrader) FormatQuantity(symbol string, quantity float64) (st...
  method GetOrderStatus (line 502) | func (t *GateTrader) GetOrderStatus(symbol string, orderID string) (map[...
  method GetOpenOrders (line 563) | func (t *GateTrader) GetOpenOrders(symbol string) ([]types.OpenOrder, er...

FILE: trader/gate/trader_test.go
  type GateTraderTestSuite (line 22) | type GateTraderTestSuite struct
    method Cleanup (line 164) | func (s *GateTraderTestSuite) Cleanup() {
  function NewGateTraderTestSuite (line 28) | func NewGateTraderTestSuite(t *testing.T) *GateTraderTestSuite {
  function TestGateTrader_InterfaceCompliance (line 176) | func TestGateTrader_InterfaceCompliance(t *testing.T) {
  function TestNewGateTrader (line 185) | func TestNewGateTrader(t *testing.T) {
  function TestGateTrader_SymbolConversion (line 229) | func TestGateTrader_SymbolConversion(t *testing.T) {
  function TestGateTrader_RevertSymbol (line 268) | func TestGateTrader_RevertSymbol(t *testing.T) {
  function TestGateTrader_CacheDuration (line 302) | func TestGateTrader_CacheDuration(t *testing.T) {
  function TestGateTrader_ClearCache (line 310) | func TestGateTrader_ClearCache(t *testing.T) {
  function TestGateTrader_MockServerResponseFormat (line 330) | func TestGateTrader_MockServerResponseFormat(t *testing.T) {

FILE: trader/grid_regime.go
  function classifyRegimeLevel (line 16) | func classifyRegimeLevel(bollingerWidth, atr14Pct float64) market.Regime...
  function getRegimeLeverageLimit (line 37) | func getRegimeLeverageLimit(level market.RegimeLevel, config *store.Grid...
  function getRegimePositionLimit (line 65) | func getRegimePositionLimit(level market.RegimeLevel, config *store.Grid...
  function detectBoxBreakout (line 98) | func detectBoxBreakout(box *market.BoxData) (market.BreakoutLevel, strin...
  constant BreakoutConfirmRequired (line 136) | BreakoutConfirmRequired = 3
  type BreakoutState (line 139) | type BreakoutState struct
  function confirmBreakout (line 147) | func confirmBreakout(state *BreakoutState, currentLevel market.BreakoutL...
  type BreakoutAction (line 175) | type BreakoutAction
  constant BreakoutActionNone (line 178) | BreakoutActionNone BreakoutAction = iota
  constant BreakoutActionReducePosition (line 179) | BreakoutActionReducePosition
  constant BreakoutActionPauseGrid (line 180) | BreakoutActionPauseGrid
  constant BreakoutActionCloseAll (line 181) | BreakoutActionCloseAll
  function getBreakoutAction (line 185) | func getBreakoutAction(level market.BreakoutLevel) BreakoutAction {
  constant BreakoutActionAdjustDirection (line 204) | BreakoutActionAdjustDirection BreakoutAction = 4
  function determineGridDirection (line 212) | func determineGridDirection(box *market.BoxData, currentDirection market...
  function determineRecoveryDirection (line 252) | func determineRecoveryDirection(price float64, box *market.BoxData, curr...
  function getBreakoutActionWithDirection (line 282) | func getBreakoutActionWithDirection(level market.BreakoutLevel, enableDi...
  function shouldRecoverDirection (line 304) | func shouldRecoverDirection(box *market.BoxData, currentDirection market...

FILE: trader/grid_regime_test.go
  function TestClassifyRegimeLevel (line 8) | func TestClassifyRegimeLevel(t *testing.T) {
  function TestDetectBoxBreakout (line 31) | func TestDetectBoxBreakout(t *testing.T) {
  function TestBreakoutConfirmation (line 70) | func TestBreakoutConfirmation(t *testing.T) {
  function TestGetBreakoutAction (line 103) | func TestGetBreakoutAction(t *testing.T) {
  function TestGetBuySellRatio (line 128) | func TestGetBuySellRatio(t *testing.T) {
  function TestDetermineGridDirection (line 161) | func TestDetermineGridDirection(t *testing.T) {
  function TestDetermineRecoveryDirection (line 237) | func TestDetermineRecoveryDirection(t *testing.T) {
  function TestGetBreakoutActionWithDirection (line 277) | func TestGetBreakoutActionWithDirection(t *testing.T) {
  function TestShouldRecoverDirection (line 307) | func TestShouldRecoverDirection(t *testing.T) {

FILE: trader/helpers.go
  function SafeFloat64 (line 9) | func SafeFloat64(data map[string]interface{}, key string) (float64, erro...
  function SafeString (line 37) | func SafeString(data map[string]interface{}, key string) (string, error) {
  function SafeInt (line 54) | func SafeInt(data map[string]interface{}, key string) (int, error) {

FILE: trader/hyperliquid/order_sync.go
  method SyncOrdersFromHyperliquid (line 17) | func (t *HyperliquidTrader) SyncOrdersFromHyperliquid(traderID string, e...
  method StartOrderSync (line 139) | func (t *HyperliquidTrader) StartOrderSync(traderID string, exchangeID s...

FILE: trader/hyperliquid/sync_test.go
  function TestHyperliquidOrderDirectionParsing (line 15) | func TestHyperliquidOrderDirectionParsing(t *testing.T) {
  function TestHyperliquidPositionBuilding (line 77) | func TestHyperliquidPositionBuilding(t *testing.T) {
  function TestHyperliquidBugScenario (line 307) | func TestHyperliquidBugScenario(t *testing.T) {

FILE: trader/hyperliquid/trader.go
  type HyperliquidTrader (line 17) | type HyperliquidTrader struct
    method FormatQuantity (line 233) | func (t *HyperliquidTrader) FormatQuantity(symbol string, quantity flo...
    method getSzDecimals (line 243) | func (t *HyperliquidTrader) getSzDecimals(coin string) int {
    method roundToSzDecimals (line 265) | func (t *HyperliquidTrader) roundToSzDecimals(coin string, quantity fl...
    method roundPriceToSigfigs (line 280) | func (t *HyperliquidTrader) roundPriceToSigfigs(price float64) float64 {
  type xyzDexMeta (line 33) | type xyzDexMeta struct
  type xyzAssetInfo (line 38) | type xyzAssetInfo struct
  function isXyzDexAsset (line 71) | func isXyzDexAsset(symbol string) bool {
  function convertSymbolToHyperliquid (line 88) | func convertSymbolToHyperliquid(symbol string) string {
  function absFloat (line 112) | func absFloat(x float64) float64 {
  function NewHyperliquidTrader (line 121) | func NewHyperliquidTrader(privateKeyHex string, walletAddr string, testn...

FILE: trader/hyperliquid/trader_account.go
  method GetBalance (line 17) | func (t *HyperliquidTrader) GetBalance() (map[string]interface{}, error) {
  type xyzDexState (line 175) | type xyzDexState struct
  type xyzMarginSummary (line 182) | type xyzMarginSummary struct
  type xyzAssetPosition (line 187) | type xyzAssetPosition struct
  method getXYZDexBalance (line 203) | func (t *HyperliquidTrader) getXYZDexBalance() (accountValue float64, un...
  method GetMarketPrice (line 270) | func (t *HyperliquidTrader) GetMarketPrice(symbol string) (float64, erro...
  method getXyzMarketPrice (line 297) | func (t *HyperliquidTrader) getXyzMarketPrice(coin string) (float64, err...
  method GetOrderStatus (line 358) | func (t *HyperliquidTrader) GetOrderStatus(symbol string, orderID string...
  method GetClosedPnL (line 405) | func (t *HyperliquidTrader) GetClosedPnL(startTime time.Time, limit int)...
  method GetTrades (line 456) | func (t *HyperliquidTrader) GetTrades(startTime time.Time, limit int) ([...
  method GetOpenOrders (line 528) | func (t *HyperliquidTrader) GetOpenOrders(symbol string) ([]types.OpenOr...
  method GetOrderBook (line 563) | func (t *HyperliquidTrader) GetOrderBook(symbol string, depth int) (bids...

FILE: trader/hyperliquid/trader_orders.go
  method OpenLong (line 19) | func (t *HyperliquidTrader) OpenLong(symbol string, quantity float64, le...
  method OpenShort (line 91) | func (t *HyperliquidTrader) OpenShort(symbol string, quantity float64, l...
  method CloseLong (line 163) | func (t *HyperliquidTrader) CloseLong(symbol string, quantity float64) (...
  method CloseShort (line 250) | func (t *HyperliquidTrader) CloseShort(symbol string, quantity float64) ...
  method CancelStopLossOrders (line 337) | func (t *HyperliquidTrader) CancelStopLossOrders(symbol string) error {
  method CancelTakeProfitOrders (line 345) | func (t *HyperliquidTrader) CancelTakeProfitOrders(symbol string) error {
  method CancelAllOrders (line 353) | func (t *HyperliquidTrader) CancelAllOrders(symbol string) error {
  method CancelStopOrders (line 385) | func (t *HyperliquidTrader) CancelStopOrders(symbol string) error {
  method cancelXyzOrders (line 427) | func (t *HyperliquidTrader) cancelXyzOrders(coin string) error {
  method cancelXyzOrder (line 495) | func (t *HyperliquidTrader) cancelXyzOrder(oid int64) error {
  function floatToWireStr (line 570) | func floatToWireStr(x float64) string {
  method placeXyzOrder (line 584) | func (t *HyperliquidTrader) placeXyzOrder(coin string, isBuy bool, size ...
  method placeXyzTriggerOrder (line 746) | func (t *HyperliquidTrader) placeXyzTriggerOrder(coin string, isBuy bool...
  method SetStopLoss (line 900) | func (t *HyperliquidTrader) SetStopLoss(symbol string, positionSide stri...
  method SetTakeProfit (line 948) | func (t *HyperliquidTrader) SetTakeProfit(symbol string, positionSide st...
  method PlaceLimitOrder (line 997) | func (t *HyperliquidTrader) PlaceLimitOrder(req *types.LimitOrderRequest...
  method CancelOrder (line 1059) | func (t *HyperliquidTrader) CancelOrder(symbol, orderID string) error {

FILE: trader/hyperliquid/trader_positions.go
  method GetPositions (line 11) | func (t *HyperliquidTrader) GetPositions() ([]map[string]interface{}, er...
  method SetMarginMode (line 142) | func (t *HyperliquidTrader) SetMarginMode(symbol string, isCrossMargin b...
  method SetLeverage (line 154) | func (t *HyperliquidTrader) SetLeverage(symbol string, leverage int) err...

FILE: trader/hyperliquid/trader_race_test.go
  function TestMetaConcurrentAccess (line 12) | func TestMetaConcurrentAccess(t *testing.T) {
  function TestMetaConcurrentReadWrite (line 46) | func TestMetaConcurrentReadWrite(t *testing.T) {
  function TestGetSzDecimals_NilMeta (line 97) | func TestGetSzDecimals_NilMeta(t *testing.T) {
  function TestGetSzDecimals_ValidMeta (line 113) | func TestGetSzDecimals_ValidMeta(t *testing.T) {
  function TestMetaMutex_NoRaceCondition (line 146) | func TestMetaMutex_NoRaceCondition(t *testing.T) {

FILE: trader/hyperliquid/trader_sync.go
  method refreshMetaIfNeeded (line 15) | func (t *HyperliquidTrader) refreshMetaIfNeeded(coin string) error {
  method fetchXyzMeta (line 50) | func (t *HyperliquidTrader) fetchXyzMeta() error {
  method getXyzSzDecimals (line 100) | func (t *HyperliquidTrader) getXyzSzDecimals(coin string) int {
  method getXyzAssetIndex (line 127) | func (t *HyperliquidTrader) getXyzAssetIndex(baseCoin string) int {

FILE: trader/indodax/trader.go
  constant indodaxBaseURL (line 21) | indodaxBaseURL    = "https://indodax.com"
  constant indodaxPublicAPI (line 22) | indodaxPublicAPI  = "/api"
  constant indodaxPrivateAPI (line 23) | indodaxPrivateAPI = "/tapi"
  type IndodaxTrader (line 30) | type IndodaxTrader struct
    method getNonce (line 105) | func (t *IndodaxTrader) getNonce() int64 {
    method sign (line 113) | func (t *IndodaxTrader) sign(body string) string {
    method doPublicRequest (line 120) | func (t *IndodaxTrader) doPublicRequest(path string) ([]byte, error) {
    method doPrivateRequest (line 147) | func (t *IndodaxTrader) doPrivateRequest(params url.Values) ([]byte, e...
    method convertSymbol (line 195) | func (t *IndodaxTrader) convertSymbol(symbol string) string {
    method convertSymbolBack (line 218) | func (t *IndodaxTrader) convertSymbolBack(indodaxSymbol string) string {
    method getCoinFromSymbol (line 224) | func (t *IndodaxTrader) getCoinFromSymbol(symbol string) string {
    method loadPairs (line 234) | func (t *IndodaxTrader) loadPairs() error {
    method getPair (line 269) | func (t *IndodaxTrader) getPair(symbol string) (*IndodaxPair, error) {
    method clearCache (line 293) | func (t *IndodaxTrader) clearCache() {
  type IndodaxPair (line 53) | type IndodaxPair struct
  type IndodaxResponse (line 70) | type IndodaxResponse struct
  type IndodaxTicker (line 78) | type IndodaxTicker struct
  type IndodaxTickerResponse (line 88) | type IndodaxTickerResponse struct
  function NewIndodaxTrader (line 93) | func NewIndodaxTrader(apiKey, secretKey string) *IndodaxTrader {
  function parseFloat (line 301) | func parseFloat(v interface{}) float64 {

FILE: trader/indodax/trader_account.go
  method GetBalance (line 15) | func (t *IndodaxTrader) GetBalance() (map[string]interface{}, error) {
  method GetPositions (line 87) | func (t *IndodaxTrader) GetPositions() ([]map[string]interface{}, error) {
  method GetClosedPnL (line 163) | func (t *IndodaxTrader) GetClosedPnL(startTime time.Time, limit int) ([]...

FILE: trader/indodax/trader_orders.go
  method OpenLong (line 15) | func (t *IndodaxTrader) OpenLong(symbol string, quantity float64, levera...
  method OpenShort (line 58) | func (t *IndodaxTrader) OpenShort(symbol string, quantity float64, lever...
  method CloseLong (line 63) | func (t *IndodaxTrader) CloseLong(symbol string, quantity float64) (map[...
  method CloseShort (line 119) | func (t *IndodaxTrader) CloseShort(symbol string, quantity float64) (map...
  method SetLeverage (line 124) | func (t *IndodaxTrader) SetLeverage(symbol string, leverage int) error {
  method SetMarginMode (line 130) | func (t *IndodaxTrader) SetMarginMode(symbol string, isCrossMargin bool)...
  method GetMarketPrice (line 136) | func (t *IndodaxTrader) GetMarketPrice(symbol string) (float64, error) {
  method SetStopLoss (line 158) | func (t *IndodaxTrader) SetStopLoss(symbol string, positionSide string, ...
  method SetTakeProfit (line 163) | func (t *IndodaxTrader) SetTakeProfit(symbol string, positionSide string...
  method CancelStopLossOrders (line 168) | func (t *IndodaxTrader) CancelStopLossOrders(symbol string) error {
  method CancelTakeProfitOrders (line 173) | func (t *IndodaxTrader) CancelTakeProfitOrders(symbol string) error {
  method CancelAllOrders (line 178) | func (t *IndodaxTrader) CancelAllOrders(symbol string) error {
  method CancelStopOrders (line 224) | func (t *IndodaxTrader) CancelStopOrders(symbol string) error {
  method FormatQuantity (line 229) | func (t *IndodaxTrader) FormatQuantity(symbol string, quantity float64) ...
  method GetOrderStatus (line 249) | func (t *IndodaxTrader) GetOrderStatus(symbol string, orderID string) (m...
  method GetOpenOrders (line 301) | func (t *IndodaxTrader) GetOpenOrders(symbol string) ([]types.OpenOrder,...

FILE: trader/indodax/trader_test.go
  function getIndodaxTestCredentials (line 12) | func getIndodaxTestCredentials(t *testing.T) (string, string) {
  function createIndodaxTestTrader (line 23) | func createIndodaxTestTrader(t *testing.T) *IndodaxTrader {
  function TestIndodaxTrader_InterfaceCompliance (line 30) | func TestIndodaxTrader_InterfaceCompliance(t *testing.T) {
  function TestNewIndodaxTrader (line 35) | func TestNewIndodaxTrader(t *tes
Condensed preview — 497 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,684K chars).
[
  {
    "path": ".dockerignore",
    "chars": 464,
    "preview": "# Git\n.git\n.gitignore\n.github\n\n# Docker\nDockerfile\ndocker-compose.yml\n.dockerignore\n\n# IDE\n.idea\n.vscode\n*.swp\n*.swo\n*~\n"
  },
  {
    "path": ".github/CLA.md",
    "chars": 1747,
    "preview": "# NOFX Contributor License Agreement\n\nThank you for your interest in contributing to NOFX. This Contributor License Agre"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 4481,
    "preview": "# CODEOWNERS\n#\n# This file defines code ownership and automatic reviewer assignment.\n# When a PR touches files matching "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bounty_claim.md",
    "chars": 1979,
    "preview": "---\nname: Bounty Claim\nabout: Claim a bounty task or propose a new bounty\ntitle: '[BOUNTY CLAIM] '\nlabels: bounty\nassign"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 4649,
    "preview": "---\nname: Bug Report\nabout: Report a bug to help us improve NOFX\ntitle: '[BUG] '\nlabels: bug\nassignees: ''\n---\n\n> **⚠️ B"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 677,
    "preview": "---\nname: Feature Request\nabout: Suggest a new feature for NOFX\ntitle: '[FEATURE] '\nlabels: enhancement\nassignees: ''\n--"
  },
  {
    "path": ".github/PR_TITLE_GUIDE.md",
    "chars": 8134,
    "preview": "# PR Title Guide\n\n## 📋 Overview\n\nWe use the **Conventional Commits** format to maintain consistency in PR titles, but th"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/README.md",
    "chars": 4453,
    "preview": "# PR Templates\n\n## 📋 Template Overview\n\nWe offer 4 specialized templates for different types of PRs to help contributors"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/backend.md",
    "chars": 1986,
    "preview": "# Pull Request - Backend\n\n> **💡 Tip:** Recommended PR title format `type(scope): description`\n> Example: `feat(trader): "
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/docs.md",
    "chars": 1611,
    "preview": "# Pull Request - Documentation\n\n> **💡 Tip:** Recommended PR title format `docs(scope): description`\n> Example: `docs(api"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/frontend.md",
    "chars": 1878,
    "preview": "# Pull Request - Frontend\n\n> **💡 Tip:** Recommended PR title format `type(scope): description`\n> Example: `feat(ui): add"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/general.md",
    "chars": 1501,
    "preview": "# Pull Request - General\n\n> **💡 Tip:** Recommended PR title format `type(scope): description`\n> Example: `feat(trader): "
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 853,
    "preview": "## Summary\n\n- Problem:\n- What changed:\n- What did NOT change (scope boundary):\n\n## Change Type\n\n- [ ] Bug fix\n- [ ] Feat"
  },
  {
    "path": ".github/SECURITY.md",
    "chars": 7800,
    "preview": "# Security Policy\n\n## 🔒 Security at NOFX\n\nWe take the security of NOFX seriously. This document outlines our security po"
  },
  {
    "path": ".github/labeler.yml",
    "chars": 2552,
    "preview": "# Auto-labeler configuration\n# Automatically adds labels based on changed files\n\n# Area: Frontend\n'area: frontend':\n  - "
  },
  {
    "path": ".github/labels.yml",
    "chars": 4013,
    "preview": "# GitHub Labels Configuration\n# Use https://github.com/crazy-max/ghaction-github-labeler to sync labels\n\n# Priority Labe"
  },
  {
    "path": ".github/workflows/README.md",
    "chars": 5979,
    "preview": "# GitHub Actions Workflows\n\nThis directory contains the GitHub Actions workflows for the NOFX project.\n\n## 📚 Documentati"
  },
  {
    "path": ".github/workflows/docker-build.yml",
    "chars": 6857,
    "preview": "name: Build and Push Docker Images\n\non:\n  push:\n    branches:\n      - main\n      - dev\n      - release/stable\n    tags:\n"
  },
  {
    "path": ".github/workflows/pr-checks-comment.yml",
    "chars": 15780,
    "preview": "name: PR Checks - Comment\n\n# This workflow posts ADVISORY check results as comments\n# Runs in the main repo context with"
  },
  {
    "path": ".github/workflows/pr-checks-run.yml",
    "chars": 4888,
    "preview": "name: PR Checks - Run\n\n# This workflow runs advisory PR checks with read-only permissions\n# Safe for fork PRs - results "
  },
  {
    "path": ".github/workflows/pr-checks.yml",
    "chars": 11740,
    "preview": "name: PR Checks\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened, edited]\n    branches:\n      - dev\n      "
  },
  {
    "path": ".github/workflows/pr-docker-check.yml",
    "chars": 8782,
    "preview": "name: PR Docker Build Check\n\n# Lightweight build check on PR only, no image push\n# Strategy: Quick verify amd64 + spot c"
  },
  {
    "path": ".github/workflows/pr-docker-compose-healthcheck.yml",
    "chars": 4954,
    "preview": "name: PR Docker Compose Healthcheck\n\n# Verify docker-compose.yml healthcheck config works correctly in Alpine containers"
  },
  {
    "path": ".github/workflows/pr-go-test-coverage.yml",
    "chars": 2363,
    "preview": "name: Go Test Coverage\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened]\n    branches:\n      - dev\n      -"
  },
  {
    "path": ".github/workflows/pr-template-suggester.yml",
    "chars": 3703,
    "preview": "name: PR Labeler\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened]\n\npermissions:\n  pull-requests: write\n  "
  },
  {
    "path": ".github/workflows/scripts/calculate_coverage.py",
    "chars": 5589,
    "preview": "#!/usr/bin/env python3\n\"\"\"\nCalculate Go test coverage and generate reports.\n\nThis script parses the coverage.out file ge"
  },
  {
    "path": ".github/workflows/scripts/comment_pr.py",
    "chars": 7304,
    "preview": "#!/usr/bin/env python3\n\"\"\"\nPost or update coverage report comment on GitHub Pull Request.\n\nThis script generates a forma"
  },
  {
    "path": ".github/workflows/scripts/requirements.txt",
    "chars": 66,
    "preview": "# Python dependencies for GitHub Actions scripts\nrequests>=2.31.0\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 1136,
    "preview": "name: Test\n\non:\n  push:\n    branches: [main, dev]\n  pull_request:\n    branches: [main, dev]\n\njobs:\n  backend-tests:\n    "
  },
  {
    "path": ".gitignore",
    "chars": 1066,
    "preview": "# IDE 配置文件\n.idea/\n*.iml\n*.xml\n\n# AI 工具\n.claude/\nCLAUDE.md\n\n# 编译产物\nnofx-auto\n*.exe\nnofx\nnofx_test\n\n# Go 相关\n*.test\n*.out\n."
  },
  {
    "path": ".husky/_/husky.sh",
    "chars": 717,
    "preview": "#!/usr/bin/env sh\nif [ -z \"$husky_skip_init\" ]; then\n  debug () {\n    if [ \"$HUSKY_DEBUG\" = \"1\" ]; then\n      echo \"husk"
  },
  {
    "path": ".husky/pre-commit",
    "chars": 79,
    "preview": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\ncd web && npx lint-staged\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 6716,
    "preview": "# Changelog\n\nAll notable changes to the NOFX project will be documented in this file.\n\nThe format is based on [Keep a Ch"
  },
  {
    "path": "CHANGELOG.zh-CN.md",
    "chars": 3096,
    "preview": "# 更新日志\n\nNOFX 项目的所有重要更改都将记录在此文件中。\n\n本文件格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),\n本项目遵循 [语义化版本](htt"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 7625,
    "preview": "# Contributor Covenant Code of Conduct / 贡献者公约行为准则\n\n**Languages:** [English](#english) | [中文](#中文)\n\n---\n\n# English\n\n## O"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 11043,
    "preview": "# 🤝 Contributing to NOFX\n\n**Language:** [English](CONTRIBUTING.md) | [中文](docs/i18n/zh-CN/CONTRIBUTING.md)\n\nThank you fo"
  },
  {
    "path": "DISCLAIMER.md",
    "chars": 10042,
    "preview": "# ⚠️ Disclaimer / 免责声明\n\n**Last Updated / 最后更新**: January 2025\n\n---\n\n## 🇬🇧 English Version\n\n### Important Legal Disclaime"
  },
  {
    "path": "Dockerfile.railway",
    "chars": 1169,
    "preview": "# Railway All-in-One: Reuse existing GHCR images\n# Extract content from existing images and merge into a single containe"
  },
  {
    "path": "ENCRYPTION_README.md",
    "chars": 3316,
    "preview": "# 🔐 End-to-End Encryption System\n\n## Quick Start (5 Minutes)\n\n```bash\n# 1. Deploy encryption system\n./deploy_encryption."
  },
  {
    "path": "LICENSE",
    "chars": 34523,
    "preview": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C)"
  },
  {
    "path": "Makefile",
    "chars": 4009,
    "preview": "# NOFX Makefile for testing and development\n\n.PHONY: help test test-backend test-frontend test-coverage clean\n\n# Default"
  },
  {
    "path": "README.ja.md",
    "chars": 36039,
    "preview": "# 🤖 NOFX - Agentic Trading OS\n\n[![Go Version](https://img.shields.io/badge/Go-1.21+-00ADD8?style=flat&logo=go)](https://"
  },
  {
    "path": "README.md",
    "chars": 14916,
    "preview": "<h1 align=\"center\">NOFX</h1>\n\n<p align=\"center\">\n  <strong>Your personal AI trading assistant.</strong><br/>\n  <strong>A"
  },
  {
    "path": "SECURITY.md",
    "chars": 9627,
    "preview": "# Security Policy / 安全政策\n\n**Languages:** [English](#english) | [中文](#中文)\n\n---\n\n# English\n\n## 🛡️ Security Overview\n\nNOFX "
  },
  {
    "path": "api/crypto_handler.go",
    "chars": 2454,
    "preview": "package api\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"nofx/config\"\n\t\"nofx/crypto\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// CryptoHandler "
  },
  {
    "path": "api/errors.go",
    "chars": 2704,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"nofx/logger\"\n)\n\n// SafeError returns a safe "
  },
  {
    "path": "api/handler_ai_model.go",
    "chars": 8229,
    "preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"nofx/config\"\n\t\"nofx/crypto\"\n\t\"nofx/logger\"\n\t\"nof"
  },
  {
    "path": "api/handler_competition.go",
    "chars": 14243,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"nofx/logger\"\n\t\"nofx/store\"\n\n\t\"github.com/gin-g"
  },
  {
    "path": "api/handler_exchange.go",
    "chars": 14078,
    "preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"nofx/config\"\n\t\"nofx/crypto\"\n\t\"nofx/logger\"\n\n\t\"github.com/gi"
  },
  {
    "path": "api/handler_klines.go",
    "chars": 11812,
    "preview": "package api\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"nofx/logger\"\n\t\"nofx/market\"\n\t\"nofx/"
  },
  {
    "path": "api/handler_order.go",
    "chars": 10257,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"strconv\"\n\n\t\"nofx/logger\"\n\t\"nofx/market\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// handleTra"
  },
  {
    "path": "api/handler_telegram.go",
    "chars": 3013,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// handleGetTelegramConfig returns current Telegram bo"
  },
  {
    "path": "api/handler_trader.go",
    "chars": 21149,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"nofx/logger\"\n\t\"nofx/store\"\n\t\"nofx/trader\"\n\t\"nofx/trader/a"
  },
  {
    "path": "api/handler_trader_config.go",
    "chars": 2182,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\n\t\"nofx/logger\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// handleUpdateTraderPrompt Update tra"
  },
  {
    "path": "api/handler_trader_status.go",
    "chars": 17976,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"nofx/logger\"\n\t\"nofx/store\"\n\t\"nofx/trader\"\n\t\"nofx/trader/a"
  },
  {
    "path": "api/handler_user.go",
    "chars": 6282,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"nofx/auth\"\n\t\"nofx/logger\"\n\t\"nofx/store\"\n\n\t\"github.com/gin-gonic/"
  },
  {
    "path": "api/handler_wallet.go",
    "chars": 3869,
    "preview": "package api\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/big\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n"
  },
  {
    "path": "api/route_registry.go",
    "chars": 1936,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// RouteDoc holds documentation for a single API"
  },
  {
    "path": "api/server.go",
    "chars": 29783,
    "preview": "package api\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"nofx/auth\"\n\t\"nofx/crypto\"\n\t\"nofx/logger\"\n\t\"nofx/manager\"\n\t\""
  },
  {
    "path": "api/server_test.go",
    "chars": 10381,
    "preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"nofx/store\"\n)\n\n// TestUpdateTraderRequest_SystemPromptTemplate Test"
  },
  {
    "path": "api/strategy.go",
    "chars": 19158,
    "preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"nofx/kernel\"\n\t\"nofx/logger\"\n\t\"nofx/market\"\n\t\"nofx/mcp\"\n\t_ \"n"
  },
  {
    "path": "api/traderid_test.go",
    "chars": 3679,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/uuid\"\n)\n\n// TestTraderIDUniqueness Test traderID"
  },
  {
    "path": "api/utils.go",
    "chars": 3288,
    "preview": "package api\n\nimport \"strings\"\n\n// MaskSensitiveString Mask sensitive strings, showing only first 4 and last 4 characters"
  },
  {
    "path": "api/utils_test.go",
    "chars": 5030,
    "preview": "package api\n\nimport (\n\t\"testing\"\n)\n\nfunc TestMaskSensitiveString(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n"
  },
  {
    "path": "auth/auth.go",
    "chars": 3267,
    "preview": "package auth\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n/"
  },
  {
    "path": "config/config.go",
    "chars": 3972,
    "preview": "package config\n\nimport (\n\t\"nofx/telemetry\"\n\t\"nofx/mcp\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Global configuration instance\nv"
  },
  {
    "path": "crypto/crypto.go",
    "chars": 12091,
    "preview": "package crypto\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/sha256\"\n\t\"crypto/x509\"\n\t\"d"
  },
  {
    "path": "docker/Dockerfile.backend",
    "chars": 2440,
    "preview": "# docker/backend/Dockerfile\n\n# ═══════════════════════════════════════════════════════════════\n# NOFX Backend Dockerfile"
  },
  {
    "path": "docker/Dockerfile.frontend",
    "chars": 1079,
    "preview": "# docker/frontend/Dockerfile\n\n# ═══════════════════════════════════════════════════════════════\n# NOFX Frontend Dockerfi"
  },
  {
    "path": "docker-compose.prod.yml",
    "chars": 1459,
    "preview": "# NOFX Production Deployment\n# 用户部署专用 - 使用官方预构建镜像\n#\n# 一键部署命令:\n# curl -fsSL https://raw.githubusercontent.com/NoFxAiOS/no"
  },
  {
    "path": "docker-compose.stable.yml",
    "chars": 1187,
    "preview": "# NOFX Stable Release Deployment\n# Production-ready stable version\n\nservices:\n  nofx:\n    image: ghcr.io/nofxaios/nofx/n"
  },
  {
    "path": "docker-compose.yml",
    "chars": 1338,
    "preview": "services:\n  # Backend service (API and core logic)\n  nofx:\n    build:\n      context: .\n      dockerfile: ./docker/Docker"
  },
  {
    "path": "docs/Git工作流规范.md",
    "chars": 8466,
    "preview": "# Git 工作流规范\n\n## 1. 总体目标\n\n建立清晰、统一的 Git 工作流规范,解决团队协作中的冲突和代码丢失问题,同时有效管理开源和闭源两个版本的代码。\n\n## 2. 分支管理策略\n\n### 2.1 开源版本(GitHub Flo"
  },
  {
    "path": "docs/MIGRATION_GUIDE.md",
    "chars": 5846,
    "preview": "# 📦 Documentation Migration Guide\n\n## What Changed?\n\nNOFX documentation has been reorganized into a structured `docs/` d"
  },
  {
    "path": "docs/README.md",
    "chars": 7639,
    "preview": "# 📚 NOFX Documentation Center / 文档中心\n\nWelcome to the NOFX documentation! This page helps you find the right documentatio"
  },
  {
    "path": "docs/api/API_REFERENCE.md",
    "chars": 16880,
    "preview": "# CryptoMaster API 接口文档\n\n## 概述\n\n### 基础信息\n- **Base URL**: `https://nofxos.ai`\n- **响应格式**: JSON\n- **缓存时间**: 15秒(所有数据接口)\n- "
  },
  {
    "path": "docs/architecture/README.md",
    "chars": 4845,
    "preview": "# NOFX Architecture Documentation\n\n**Language:** [English](README.md) | [中文](README.zh-CN.md)\n\nTechnical documentation f"
  },
  {
    "path": "docs/architecture/README.zh-CN.md",
    "chars": 3856,
    "preview": "# NOFX 架构文档\n\n**语言:** [English](README.md) | [中文](README.zh-CN.md)\n\n为希望了解 NOFX 内部实现的开发者提供的技术文档。\n\n---\n\n## 概述\n\nNOFX 是一个支持加密"
  },
  {
    "path": "docs/architecture/STRATEGY_MODULE.md",
    "chars": 21178,
    "preview": "# NOFX Strategy Module - Technical Documentation\n\n**Language:** [English](STRATEGY_MODULE.md) | [中文](STRATEGY_MODULE.zh-"
  },
  {
    "path": "docs/architecture/STRATEGY_MODULE.zh-CN.md",
    "chars": 17864,
    "preview": "# NOFX 策略模块技术文档\n\n**语言:** [English](STRATEGY_MODULE.md) | [中文](STRATEGY_MODULE.zh-CN.md)\n\n## 概述\n\n本文档详细描述 NOFX 策略模块的完整数据流程"
  },
  {
    "path": "docs/architecture/X402_STREAMING_PAYMENT.md",
    "chars": 11649,
    "preview": "# x402 Streaming Payment Architecture\n\n## Overview\n\nNOFX calls AI models (DeepSeek, GPT, Claude, etc.) through the claw4"
  },
  {
    "path": "docs/community/HOW_TO_MIGRATE_YOUR_PR.md",
    "chars": 5328,
    "preview": "# 🔄 How to Migrate Your PR to the New Format\n\n**Language:** [English](HOW_TO_MIGRATE_YOUR_PR.md) | [中文](HOW_TO_MIGRATE_Y"
  },
  {
    "path": "docs/community/HOW_TO_MIGRATE_YOUR_PR.zh-CN.md",
    "chars": 3460,
    "preview": "# 🔄 如何将你的 PR 迁移到新格式\n\n**语言:** [English](HOW_TO_MIGRATE_YOUR_PR.md) | [中文](HOW_TO_MIGRATE_YOUR_PR.zh-CN.md)\n\n本指南帮助你将现有 PR "
  },
  {
    "path": "docs/community/MIGRATION_ANNOUNCEMENT.md",
    "chars": 9284,
    "preview": "# 📢 PR Management System Update - What Contributors Need to Know\n\n**Language:** [English](MIGRATION_ANNOUNCEMENT.md) | ["
  },
  {
    "path": "docs/community/MIGRATION_ANNOUNCEMENT.zh-CN.md",
    "chars": 5308,
    "preview": "# 📢 PR 管理系统更新 - 贡献者须知\n\n**语言:** [English](MIGRATION_ANNOUNCEMENT.md) | [中文](MIGRATION_ANNOUNCEMENT.zh-CN.md)\n\n我们正在引入新的 PR"
  },
  {
    "path": "docs/community/OFFICIAL_ACCOUNTS.md",
    "chars": 1969,
    "preview": "# ⚠️ Official Accounts & Anti-Impersonation Notice\n\n## Legal Entity\n\n| Field | Details |\n|-------|---------|\n| Company N"
  },
  {
    "path": "docs/community/PR_COMMENT_TEMPLATE.md",
    "chars": 3637,
    "preview": "# 📢 PR Comment Template for Existing PRs\n\nThis template is for maintainers to comment on existing PRs to introduce the n"
  },
  {
    "path": "docs/community/README.md",
    "chars": 6763,
    "preview": "# 👥 NOFX Community\n\nWelcome to the NOFX community! This section contains everything you need to contribute and participa"
  },
  {
    "path": "docs/community/bounty-aster.md",
    "chars": 4712,
    "preview": "# 🚀 [BOUNTY] Integrate Aster Exchange Support\n\n## 💰 Bounty Reward\n**To be discussed** - Open to proposals from contribut"
  },
  {
    "path": "docs/community/bounty-guide.md",
    "chars": 3441,
    "preview": "# 📝 如何在 GitHub 发布集成任务 (Bounty)\n\n## 🎯 发布步骤\n\n### 方法 1: 直接创建 GitHub Issue(推荐)\n\n1. **访问项目 Issues 页面**\n   ```\n   https://gith"
  },
  {
    "path": "docs/community/bounty-hyperliquid.md",
    "chars": 4463,
    "preview": "# 🚀 [BOUNTY] Integrate Hyperliquid Exchange Support\n\n## 💰 Bounty Reward\n**To be discussed** - Open to proposals from con"
  },
  {
    "path": "docs/getting-started/README.md",
    "chars": 3485,
    "preview": "# 🚀 Getting Started with NOFX\n\n**Language:** [English](README.md) | [中文](README.zh-CN.md)\n\nThis section contains all the"
  },
  {
    "path": "docs/getting-started/README.zh-CN.md",
    "chars": 1487,
    "preview": "# 🚀 NOFX 快速开始\n\n本节包含让 NOFX 运行起来所需的所有文档。\n\n## 📋 部署选项\n\n选择最适合您的方式:\n\n### 🐳 Docker 部署(推荐)\n\n**适合:** 初学者、快速部署、生产环境\n\n- **中文文档:** ["
  },
  {
    "path": "docs/getting-started/aster-api-wallet.md",
    "chars": 3027,
    "preview": "# Aster DEX API Wallet Setup Guide\n\nThis guide explains how to create and configure an API Wallet for secure trading on "
  },
  {
    "path": "docs/getting-started/binance-api.md",
    "chars": 1785,
    "preview": "# Binance API Setup Guide\n\nThis guide explains how to create and configure Binance API keys for use with NOFX.\n\n## Creat"
  },
  {
    "path": "docs/getting-started/blockrun-base-wallet.md",
    "chars": 4927,
    "preview": "# BlockRun Base (EVM) Wallet Setup Guide\n\nThis guide explains how to use a Base network EVM wallet to pay for AI usage t"
  },
  {
    "path": "docs/getting-started/blockrun-sol-wallet.md",
    "chars": 4770,
    "preview": "# BlockRun Solana Wallet Setup Guide\n\nThis guide explains how to use a Solana wallet to pay for AI usage through BlockRu"
  },
  {
    "path": "docs/getting-started/bybit-api.md",
    "chars": 1712,
    "preview": "# Bybit API Setup Guide\n\nThis guide explains how to create and configure Bybit API keys for use with NOFX.\n\n## Create AP"
  },
  {
    "path": "docs/getting-started/custom-api.en.md",
    "chars": 5617,
    "preview": "# Custom AI API Usage Guide\n\n## Features\n\nNOFX now supports using any OpenAI-compatible API format, including:\n- OpenAI "
  },
  {
    "path": "docs/getting-started/custom-api.md",
    "chars": 4172,
    "preview": "# 自定义 AI API 使用指南\n\n## 功能说明\n\n现在 NOFX 支持使用任何 OpenAI 格式兼容的 API,包括:\n- OpenAI 官方 API (gpt-4o, gpt-4-turbo 等)\n- OpenRouter (可访"
  },
  {
    "path": "docs/getting-started/hyperliquid-agent-wallet.md",
    "chars": 2860,
    "preview": "# Hyperliquid Agent Wallet Setup Guide\n\nThis guide explains how to create and configure an Agent Wallet for secure tradi"
  },
  {
    "path": "docs/getting-started/lighter-agent-wallet.md",
    "chars": 2403,
    "preview": "# Lighter Agent Wallet Setup Guide\n\nThis guide explains how to create and configure an Agent Wallet for secure trading o"
  },
  {
    "path": "docs/getting-started/okx-api.md",
    "chars": 1971,
    "preview": "# OKX API Setup Guide\n\nThis guide explains how to create and configure OKX API keys for use with NOFX.\n\n## Create API Ke"
  },
  {
    "path": "docs/guides/README.md",
    "chars": 3104,
    "preview": "# 📘 NOFX User Guides\n\n**Language:** [English](README.md) | [中文](README.zh-CN.md)\n\nComprehensive guides to help you use N"
  },
  {
    "path": "docs/guides/README.zh-CN.md",
    "chars": 1807,
    "preview": "# 📘 NOFX 使用指南\n\n**语言:** [English](README.md) | [中文](README.zh-CN.md)\n\n帮助您有效使用 NOFX 的综合指南。\n\n---\n\n## 📚 可用指南\n\n### 🔧 基础使用\n\n| "
  },
  {
    "path": "docs/guides/TROUBLESHOOTING.md",
    "chars": 14177,
    "preview": "# 🔧 Troubleshooting Guide\n\nThis guide helps you diagnose and fix common issues before submitting a bug report.\n\n---\n\n## "
  },
  {
    "path": "docs/guides/TROUBLESHOOTING.zh-CN.md",
    "chars": 9429,
    "preview": "# 🔧 故障排查指南\n\n本指南帮助您在提交 bug 报告前自行诊断和修复常见问题。\n\n---\n\n## 📋 快速诊断清单\n\n提交 bug 前,请检查:\n\n1. ✅ **后端正在运行**: `docker compose ps` 或 `ps a"
  },
  {
    "path": "docs/guides/faq.en.md",
    "chars": 6039,
    "preview": "# Frequently Asked Questions (FAQ)\n\nQuick answers to common questions. For detailed troubleshooting, see [Troubleshootin"
  },
  {
    "path": "docs/guides/faq.zh-CN.md",
    "chars": 3412,
    "preview": "# 常见问题(FAQ)\n\n快速解答常见问题。详细故障排查请参考[故障排查指南](TROUBLESHOOTING.zh-CN.md)。\n\n---\n\n## 基础问题\n\n### NOFX 是什么?\nNOFX 是一个 AI 驱动的加密货币交易机器人"
  },
  {
    "path": "docs/i18n/README.md",
    "chars": 5947,
    "preview": "# 🌍 International Documentation / 国际化文档\n\nNOFX documentation is available in multiple languages.\n\nNOFX 文档提供多种语言版本。\n\n---\n\n"
  },
  {
    "path": "docs/i18n/en/PRIVACY POLICY.md",
    "chars": 8620,
    "preview": "NOFX Privacy Policy\n\nLast Updated: 2025.11.07\n\nI. Introduction and Scope\n\n\nA. Introduction\n\nThis Privacy Policy (hereina"
  },
  {
    "path": "docs/i18n/en/TERMS OF SERVICE.md",
    "chars": 13196,
    "preview": "NOFX Terms of Service\n\nLast Updated: November 7, 2025\n\n1. Introduction and Acceptance of Terms\n\n\nA. Agreement\n\nThese Ter"
  },
  {
    "path": "docs/i18n/ja/PRIVACY POLICY.md",
    "chars": 4344,
    "preview": "NOFXプライバシーポリシー\n\n最終更新日: 2025.11.07\n\nI. はじめに及び適用範囲\n\n\nA. 導入\n\n本プライバシーポリシー(以下「本ポリシー」といいます)は、当社のウェブサイトのユーザーである皆様に対して、個人情報をどのよう"
  },
  {
    "path": "docs/i18n/ja/README.md",
    "chars": 8136,
    "preview": "<h1 align=\"center\">NOFX</h1>\n\n<p align=\"center\">\n  <strong>あなた専属の AI トレーディングアシスタント。</strong><br/>\n  <strong>あらゆる市場。あらゆるモ"
  },
  {
    "path": "docs/i18n/ja/TERMS OF SERVICE.md",
    "chars": 5896,
    "preview": "NOFX 利用規約(サービス利用規約)\n\n最終更新日:2025年11月7日\n\n1. はじめにと規約の承諾\n\n\nA. 本契約\n\n本利用規約(以下「本契約」または「本規約」)は、お客様(以下「お客様」または「ユーザー」)とNOFX(以下「当社」"
  },
  {
    "path": "docs/i18n/ko/README.md",
    "chars": 8057,
    "preview": "<h1 align=\"center\">NOFX</h1>\n\n<p align=\"center\">\n  <strong>당신만의 AI 트레이딩 어시스턴트.</strong><br/>\n  <strong>모든 시장. 모든 모델. API"
  },
  {
    "path": "docs/i18n/ru/PRIVACY POLICY.md",
    "chars": 9737,
    "preview": "Политика конфиденциальности NOFX\n\nПоследнее обновление: 2025.11.07\n\nI. Введение и область применения\n\n\nA. Введение\n\nНаст"
  },
  {
    "path": "docs/i18n/ru/README.md",
    "chars": 9410,
    "preview": "<h1 align=\"center\">NOFX</h1>\n\n<p align=\"center\">\n  <strong>Ваш персональный AI торговый ассистент.</strong><br/>\n  <stro"
  },
  {
    "path": "docs/i18n/ru/TERMS OF SERVICE.md",
    "chars": 14575,
    "preview": "Пользовательское соглашение NOFX (Условия предоставления услуг)\n\nПоследнее обновление: 07.11.2025\n\n1. Введение и приняти"
  },
  {
    "path": "docs/i18n/uk/PRIVACY POLICY.md",
    "chars": 9284,
    "preview": "Політика конфіденційності NOFX\n\nОстаннє оновлення: 2025.11.07\n\nI. Вступ та сфера застосування\n\n\nA. Вступ\n\nЦя Політика ко"
  },
  {
    "path": "docs/i18n/uk/README.md",
    "chars": 9258,
    "preview": "<h1 align=\"center\">NOFX</h1>\n\n<p align=\"center\">\n  <strong>Ваш персональний AI торговий асистент.</strong><br/>\n  <stron"
  },
  {
    "path": "docs/i18n/uk/TERMS OF SERVICE.md",
    "chars": 13775,
    "preview": "Угода користувача NOFX (Умови надання послуг)\n\nОстання дата оновлення: 07.11.2025\n\n1. Вступ та прийняття умов\n\n\nA. Угода"
  },
  {
    "path": "docs/i18n/vi/README.md",
    "chars": 9065,
    "preview": "<h1 align=\"center\">NOFX</h1>\n\n<p align=\"center\">\n  <strong>Trợ lý giao dịch AI cá nhân của bạn.</strong><br/>\n  <strong>"
  },
  {
    "path": "docs/i18n/zh-CN/CONTRIBUTING.md",
    "chars": 7380,
    "preview": "# 🤝 为 NOFX 做贡献\n\n**语言:** [English](../../../CONTRIBUTING.md) | [中文](CONTRIBUTING.md)\n\n> **语言声明:** 本中文版本文档仅为方便海外华人社区阅读而提供,"
  },
  {
    "path": "docs/i18n/zh-CN/PRIVACY POLICY.md",
    "chars": 3388,
    "preview": "NOFX 隐私政策\n\n最后更新时间:2025.11.07\n\n**语言声明:本中文版本文档仅为方便海外华人社区阅读而提供,不代表本软件面向中国大陆、香港、澳门或台湾地区用户开放。如您位于上述地区,请勿使用本软件。**\n\n一、 引言与范围\n\n\n"
  },
  {
    "path": "docs/i18n/zh-CN/README.md",
    "chars": 9042,
    "preview": "<h1 align=\"center\">NOFX</h1>\n\n<p align=\"center\">\n  <strong>你的个人 AI 交易助手。</strong><br/>\n  <strong>任何市场。任何模型。用 USDC 付费,无需 "
  },
  {
    "path": "docs/i18n/zh-CN/TERMS OF SERVICE.md",
    "chars": 4556,
    "preview": "NOFX 用户协议(服务条款)\n\n最后更新时间:2025.11.07\n\n**语言声明:本中文版本文档仅为方便海外华人社区阅读而提供,不代表本软件面向中国大陆、香港、澳门或台湾地区用户开放。如您位于上述地区,请勿使用本软件。**\n\n一、 引言"
  },
  {
    "path": "docs/legal/AGPL-VIOLATION-REPORT-ChainOpera-EN.md",
    "chars": 11596,
    "preview": "# AGPL Violation Evidence Report: ChainOpera Plagiarized NOFX\n\n**Report Date**: December 20, 2025\n**Reporting Party**: N"
  },
  {
    "path": "docs/legal/AGPL-VIOLATION-REPORT-ChainOpera.md",
    "chars": 7602,
    "preview": "# AGPL 违规证据报告:ChainOpera 抄袭 NOFX\n\n**报告日期**:2025年12月20日\n**报告方**:NOFX 开源社区\n**项目地址**:https://github.com/NoFxAiOS/nofx\n**被指控"
  },
  {
    "path": "docs/maintainers/PROJECT_MANAGEMENT.md",
    "chars": 9038,
    "preview": "# 📊 Project Management Guide\n\n**Language:** [English](PROJECT_MANAGEMENT.md) | [中文](PROJECT_MANAGEMENT.zh-CN.md)\n\nThis g"
  },
  {
    "path": "docs/maintainers/PROJECT_MANAGEMENT.zh-CN.md",
    "chars": 5348,
    "preview": "# 📊 项目管理指南\n\n**语言:** [English](PROJECT_MANAGEMENT.md) | [中文](PROJECT_MANAGEMENT.zh-CN.md)\n\n本指南解释了我们如何管理 NOFX 项目、跟踪进度和优先级排"
  },
  {
    "path": "docs/maintainers/PR_REVIEW_GUIDE.md",
    "chars": 10762,
    "preview": "# 🔍 PR Review Guide for Maintainers\n\n**Language:** [English](PR_REVIEW_GUIDE.md) | [中文](PR_REVIEW_GUIDE.zh-CN.md)\n\nThis "
  },
  {
    "path": "docs/maintainers/PR_REVIEW_GUIDE.zh-CN.md",
    "chars": 6178,
    "preview": "# 🔍 维护者 PR 审核指南\n\n**语言:** [English](PR_REVIEW_GUIDE.md) | [中文](PR_REVIEW_GUIDE.zh-CN.md)\n\n本指南适用于审核 pull request 的 NOFX 维护"
  },
  {
    "path": "docs/maintainers/README.md",
    "chars": 1589,
    "preview": "# 📚 Maintainer Documentation\n\n**Language:** [English](README.md) | [中文](README.zh-CN.md)\n\nThis directory contains docume"
  },
  {
    "path": "docs/maintainers/README.zh-CN.md",
    "chars": 817,
    "preview": "# 📚 维护者文档\n\n**语言:** [English](README.md) | [中文](README.zh-CN.md)\n\n此目录包含 NOFX 项目维护者和想要了解我们流程的贡献者的文档。\n\n---\n\n## 📖 文档\n\n| 文档 |"
  },
  {
    "path": "docs/maintainers/SETUP_GUIDE.md",
    "chars": 9429,
    "preview": "# 🚀 PR Management System Setup Guide\n\n**Language:** [English](SETUP_GUIDE.md) | [中文](SETUP_GUIDE.zh-CN.md)\n\nThis guide w"
  },
  {
    "path": "docs/maintainers/SETUP_GUIDE.zh-CN.md",
    "chars": 6651,
    "preview": "# 🚀 PR 管理系统设置指南\n\n**语言:** [English](SETUP_GUIDE.md) | [中文](SETUP_GUIDE.zh-CN.md)\n\n本指南将帮助你为 NOFX 设置和激活完整的 PR 管理系统。\n\n---\n\n#"
  },
  {
    "path": "docs/market-regime-classification-en.md",
    "chars": 12066,
    "preview": "# Market Regime Classification Framework\n\n> A comprehensive market state identification system for quantitative trading "
  },
  {
    "path": "docs/market-regime-classification-zh.md",
    "chars": 6442,
    "preview": "# 市场行情精细分类体系\n\n> 用于量化交易策略匹配的市场状态识别框架\n\n---\n\n## 一、分类维度概览\n\n市场状态识别需要从多个维度进行分析:\n\n| 维度 | 子维度 | 说明 |\n|------|--------|------|\n| "
  },
  {
    "path": "docs/plans/2026-01-14-grid-trading-fixes.md",
    "chars": 27751,
    "preview": "# AI自适应网格交易系统修复计划\n\n> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-"
  },
  {
    "path": "docs/plans/2026-01-17-grid-market-regime-design.md",
    "chars": 2828,
    "preview": "# 网格策略市场状态识别与风控设计\n\n## 概述\n\n增强网格策略的市场状态识别能力,实现震荡/趋势的精准判断,并根据不同震荡级别自动调整网格参数和风控策略。\n\n---\n\n## 一、市场状态识别\n\n### 1.1 识别维度(3个)\n\n| 维度"
  },
  {
    "path": "docs/plans/2026-01-17-grid-market-regime-impl.md",
    "chars": 44973,
    "preview": "# Grid Market Regime Detection Implementation Plan\n\n> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plan"
  },
  {
    "path": "docs/plans/2026-03-06-telegram-agent-redesign.md",
    "chars": 34446,
    "preview": "# Telegram Bot Agent Redesign (OpenClaw-Inspired)\n\n> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven"
  },
  {
    "path": "docs/plans/2026-03-06-telegram-bot.md",
    "chars": 28892,
    "preview": "# Telegram Bot Integration Implementation Plan\n\n> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to"
  },
  {
    "path": "docs/pnl.md",
    "chars": 5622,
    "preview": "# PNL计算重构方案 - 最终设计\n\n## 📋 核心问题与答案\n\n### 1. **Initial Balance(初始余额)**\n\n**定义:** 创建trader时的账户净值(Total Equity),作为所有PNL计算的基准\n\n*"
  },
  {
    "path": "docs/prompt-guide.md",
    "chars": 41631,
    "preview": "# 📖 NoFx Prompt Writing Guide\n\n**Version**: v1.0\n**Last Updated**: 2025-01-09\n**Compatible System Version**: NoFx v0.x+\n"
  },
  {
    "path": "docs/prompt-guide.zh-CN.md",
    "chars": 20956,
    "preview": "# 📖 NoFx Prompt 编写指南\n\n**版本**: v1.0\n**更新日期**: 2025-01-09\n**适用系统版本**: NoFx v0.x+\n\n---\n\n## 📚 目录\n\n- [🚀 快速开始](#-快速开始5分钟)\n- [💡"
  },
  {
    "path": "docs/research/AI-Trader-Analysis-Report.md",
    "chars": 73466,
    "preview": "# AI-Trader: 自主交易代理实时金融市场基准测试系统深度研究报告\n\n**项目名称:** AI-Trader: Benchmarking Autonomous Agents in Real-Time Financial Market"
  },
  {
    "path": "docs/roadmap/README.md",
    "chars": 11390,
    "preview": "# 🗺️ NOFX Roadmap\n\n**Language:** [English](README.md) | [中文](README.zh-CN.md)\n\nStrategic plan for NOFX development and u"
  },
  {
    "path": "docs/roadmap/README.zh-CN.md",
    "chars": 5310,
    "preview": "# 🗺️ NOFX 路线图\n\n**语言:** [English](README.md) | [中文](README.zh-CN.md)\n\nNOFX 发展和通用市场扩展的战略规划。\n\n---\n\n## 📋 概述\n\nNOFX 的使命是成为所有金融"
  },
  {
    "path": "go.mod",
    "chars": 6216,
    "preview": "module nofx\n\ngo 1.25.3\n\nrequire (\n\tgithub.com/adshao/go-binance/v2 v2.8.9\n\tgithub.com/agiledragon/gomonkey/v2 v2.13.0\n\tg"
  },
  {
    "path": "go.sum",
    "chars": 40313,
    "preview": "filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=\nfilippo.io/edwards25519 v1.0.0-rc.1/"
  },
  {
    "path": "hook/README.md",
    "chars": 4626,
    "preview": "# Hook 模块使用文档\n\n## 简介\n\nHook模块提供了一个通用的扩展点机制,允许在不修改核心代码的前提下注入自定义逻辑。\n\n**核心特点**:\n- 类型安全的泛型API\n- Hook未注册时自动fallback\n- 支持任意参数和返"
  },
  {
    "path": "hook/hooks.go",
    "chars": 1021,
    "preview": "package hook\n\nimport (\n\t\"log\"\n)\n\ntype HookFunc func(args ...any) any\n\nvar (\n\tHooks       map[string]HookFunc = map[strin"
  },
  {
    "path": "hook/http_client_hook.go",
    "chars": 355,
    "preview": "package hook\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\ntype SetHttpClientResult struct {\n\tErr    error\n\tClient *http.Client\n}\n\nfun"
  },
  {
    "path": "hook/ip_hook.go",
    "chars": 280,
    "preview": "package hook\n\nimport \"github.com/rs/zerolog/log\"\n\ntype IpResult struct {\n\tErr error\n\tIP  string\n}\n\nfunc (r *IpResult) Er"
  },
  {
    "path": "hook/trader_hook.go",
    "chars": 732,
    "preview": "package hook\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/adshao/go-binance/v2/futures\"\n)\n\ntype NewBinanceTraderResult str"
  },
  {
    "path": "install-stable.sh",
    "chars": 2633,
    "preview": "#!/bin/bash\n#\n# NOFX Stable Release Installation Script\n#\n# Usage:\n#   curl -fsSL https://raw.githubusercontent.com/NoFx"
  },
  {
    "path": "install.sh",
    "chars": 9322,
    "preview": "#!/bin/bash\n#\n# NOFX One-Click Installation Script\n# https://github.com/NoFxAiOS/nofx\n#\n# Usage:\n#   curl -fsSL https://"
  },
  {
    "path": "kernel/engine.go",
    "chars": 26226,
    "preview": "package kernel\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"nofx/logger\"\n\t\"nofx/market\"\n\t\"nofx/provi"
  },
  {
    "path": "kernel/engine_analysis.go",
    "chars": 12690,
    "preview": "package kernel\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"nofx/logger\"\n\t\"nofx/market\"\n\t\"nofx/mcp\"\n\t\"nofx/store\"\n\t\"regexp\"\n\t\"str"
  },
  {
    "path": "kernel/engine_position.go",
    "chars": 4275,
    "preview": "package kernel\n\nimport (\n\t\"fmt\"\n\t\"nofx/logger\"\n)\n\n// ==================================================================="
  },
  {
    "path": "kernel/engine_prompt.go",
    "chars": 28653,
    "preview": "package kernel\n\nimport (\n\t\"fmt\"\n\t\"nofx/market\"\n\t\"nofx/provider/nofxos\"\n\t\"nofx/store\"\n\t\"strings\"\n\t\"time\"\n)\n\n// =========="
  },
  {
    "path": "kernel/formatter.go",
    "chars": 20085,
    "preview": "package kernel\n\nimport (\n\t\"fmt\"\n\t\"nofx/market\"\n\t\"nofx/provider/nofxos\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n)\n\n// ================"
  },
  {
    "path": "kernel/grid_engine.go",
    "chars": 22452,
    "preview": "package kernel\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"nofx/logger\"\n\t\"nofx/market\"\n\t\"nofx/mcp\"\n\t\"nofx/store\"\n\t\"strings\"\n\t\"ti"
  },
  {
    "path": "kernel/prompt_builder.go",
    "chars": 10325,
    "preview": "package kernel\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\n// ================================================================="
  },
  {
    "path": "kernel/prompt_builder_test.go",
    "chars": 10565,
    "preview": "package kernel\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\n// TestPromptBuilder 测试提示词构建器\nfunc TestPromptBuilder(t *testin"
  },
  {
    "path": "kernel/schema.go",
    "chars": 16181,
    "preview": "package kernel\n\n// ============================================================================\n// Trading Data Schema\n/"
  },
  {
    "path": "kernel/validate_test.go",
    "chars": 3160,
    "preview": "package kernel\n\nimport (\n\t\"testing\"\n)\n\n// TestLeverageFallback tests automatic correction when leverage exceeds limit\nfu"
  },
  {
    "path": "logger/config.go",
    "chars": 295,
    "preview": "package logger\n\n// Config is the logger configuration (simplified version)\ntype Config struct {\n\tLevel string `json:\"lev"
  },
  {
    "path": "logger/logger.go",
    "chars": 5080,
    "preview": "package logger\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/sirupsen/logrus"
  },
  {
    "path": "main.go",
    "chars": 5355,
    "preview": "package main\n\nimport (\n\t\"nofx/api\"\n\t\"nofx/auth\"\n\t\"nofx/config\"\n\t\"nofx/crypto\"\n\t\"nofx/telemetry\"\n\t\"nofx/logger\"\n\t\"nofx/ma"
  },
  {
    "path": "manager/trader_manager.go",
    "chars": 23804,
    "preview": "package manager\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"nofx/logger\"\n\t\"nofx/store\"\n\t\"nofx/trader\"\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Co"
  },
  {
    "path": "market/api_client.go",
    "chars": 3406,
    "preview": "package market\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"nofx/hook\"\n\t\"strconv\"\n\t\"time\"\n)\n\nconst (\n\tba"
  },
  {
    "path": "market/data.go",
    "chars": 22072,
    "preview": "package market\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"nofx/logger\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\n/"
  },
  {
    "path": "market/data_indicators.go",
    "chars": 5717,
    "preview": "package market\n\nimport \"math\"\n\n// calculateEMA calculates EMA\nfunc calculateEMA(klines []Kline, period int) float64 {\n\ti"
  },
  {
    "path": "market/data_klines.go",
    "chars": 10921,
    "preview": "package market\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"nofx/logger\"\n\t\"nofx/provider/coinank/coinank_api\"\n\t\"nofx/provider/coinank/c"
  },
  {
    "path": "market/data_test.go",
    "chars": 16413,
    "preview": "package market\n\nimport (\n\t\"math\"\n\t\"testing\"\n)\n\n// generateTestKlines generates test K-line data\nfunc generateTestKlines("
  },
  {
    "path": "market/historical.go",
    "chars": 2376,
    "preview": "package market\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n)\n\nconst (\n\tbinanceFuturesKlinesURL = \"https:"
  },
  {
    "path": "market/timeframe.go",
    "chars": 1637,
    "preview": "package market\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n)\n\n// supportedTimeframes defines supported timeframes and "
  },
  {
    "path": "market/types.go",
    "chars": 8764,
    "preview": "package market\n\nimport \"time\"\n\n// Data market data structure\ntype Data struct {\n\tSymbol            string\n\tCurrentPrice "
  },
  {
    "path": "mcp/client.go",
    "chars": 22174,
    "preview": "package mcp\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst"
  },
  {
    "path": "mcp/client_test.go",
    "chars": 9818,
    "preview": "package mcp\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n)\n\n// ==================================================="
  },
  {
    "path": "mcp/config.go",
    "chars": 1678,
    "preview": "package mcp\n\nimport (\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"nofx/logger\"\n\t\"nofx/security\"\n)\n\n// Config client configur"
  },
  {
    "path": "mcp/config_usage_test.go",
    "chars": 7133,
    "preview": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n)\n\n// ==================="
  },
  {
    "path": "mcp/context_guard.go",
    "chars": 3906,
    "preview": "package mcp\n\nimport (\n\t\"fmt\"\n\t\"unicode/utf8\"\n)\n\n// estimateMessageTokens estimates the token count for a list of chat me"
  },
  {
    "path": "mcp/context_guard_test.go",
    "chars": 3719,
    "preview": "package mcp\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestEstimateMessageTokens(t *testing.T) {\n\tmsgs := []map[string]stri"
  },
  {
    "path": "mcp/examples_test.go",
    "chars": 7495,
    "preview": "package mcp_test\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"nofx/mcp\"\n\t\"nofx/mcp/provider\"\n)\n\n// =========================="
  },
  {
    "path": "mcp/hooks.go",
    "chars": 1553,
    "preview": "package mcp\n\nimport \"net/http\"\n\n// ClientHooks is the dispatch interface used to implement per-provider\n// polymorphism "
  },
  {
    "path": "mcp/interface.go",
    "chars": 1111,
    "preview": "package mcp\n\nimport (\n\t\"time\"\n)\n\n// ClientEmbedder is implemented by provider types that embed *Client,\n// allowing gene"
  },
  {
    "path": "mcp/intro/BUILDER_EXAMPLES.md",
    "chars": 12438,
    "preview": "# RequestBuilder 使用示例\n\n## 📋 目录\n1. [基础用法](#基础用法)\n2. [多轮对话](#多轮对话)\n3. [参数精细控制](#参数精细控制)\n4. [Function Calling](#function-ca"
  },
  {
    "path": "mcp/intro/BUILDER_PATTERN_BENEFITS.md",
    "chars": 14462,
    "preview": "# 构建器模式在 MCP 模块中的应用价值\n\n## 📋 目录\n1. [当前实现的局限性](#当前实现的局限性)\n2. [构建器模式的好处](#构建器模式的好处)\n3. [实际应用场景](#实际应用场景)\n4. [对比示例](#对比示例)\n5"
  },
  {
    "path": "mcp/intro/LOGRUS_INTEGRATION.md",
    "chars": 5579,
    "preview": "# Logrus 集成指南\n\n本文档展示如何将 MCP 模块与 Logrus 日志库集成。\n\n## 📦 安装 Logrus\n\n```bash\ngo get github.com/sirupsen/logrus\n```\n\n## 🔧 集成步骤\n"
  },
  {
    "path": "mcp/intro/MIGRATION_GUIDE.md",
    "chars": 6645,
    "preview": "# MCP 模块重构迁移指南\n\n## 📋 重构概览\n\n本次重构采用**渐进式、向前兼容**的设计,现有代码**无需修改**即可继续使用,同时提供了更强大的新 API。\n\n### 重构目标\n\n- ✅ **100% 向前兼容** - 所有现有 "
  },
  {
    "path": "mcp/intro/README.md",
    "chars": 6267,
    "preview": "# MCP - Model Context Protocol Client\n\n一个灵活、可扩展的 AI 模型客户端库,支持 DeepSeek、Qwen 等多种 AI 提供商。\n\n## ✨ 特性\n\n- 🔌 **多 Provider 支持** "
  },
  {
    "path": "mcp/logger.go",
    "chars": 821,
    "preview": "package mcp\n\n// Logger interface (abstract dependency)\n// Uses Printf-style method names for easy integration with mains"
  },
  {
    "path": "mcp/mock_test.go",
    "chars": 7445,
    "preview": "package mcp\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"sync\"\n)\n\n// ================================="
  },
  {
    "path": "mcp/options.go",
    "chars": 5022,
    "preview": "package mcp\n\nimport (\n\t\"net/http\"\n\t\"time\"\n)\n\n// ClientOption client option function (Functional Options pattern)\ntype Cl"
  },
  {
    "path": "mcp/options_test.go",
    "chars": 6711,
    "preview": "package mcp\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n)\n\n// ============================================================\n"
  },
  {
    "path": "mcp/payment/blockrun_base.go",
    "chars": 10616,
    "preview": "package payment\n\nimport (\n\t\"crypto/ecdsa\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"ma"
  }
]

// ... and 297 more files (download for full content)

About this extraction

This page contains the full source code of the NoFxAiOS/nofx GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 497 files (4.2 MB), approximately 1.1M tokens, and a symbol index with 2816 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!