# NOFX Strategy Module - Technical Documentation **Language:** [English](STRATEGY_MODULE.md) | [中文](STRATEGY_MODULE.zh-CN.md) ## Overview This document describes the complete data flow of the NOFX strategy module, including coin selection, data assembly, prompt construction, AI request, response parsing, and decision execution. --- ## Complete Data Flow ``` ┌─────────────────────────────────────────────────────────────────┐ │ Trading Cycle (Every N Minutes) │ └─────────────────────────────────────────────────────────────────┘ 1. Coin Selection (GetCandidateCoins) ├─ Static (Static list) ├─ AI500 Pool (AI rating pool) ├─ OI Top (Position growth ranking) └─ Mixed (Mixed mode) ↓ 2. Data Assembly (buildTradingContext) ├─ Account balance → equity, available, unrealizedPnL ├─ Current positions → symbol, side, entry, mark, qty, leverage ├─ K-line data → OHLCV (5m, 15m, 1h, 4h) ├─ Technical indicators → EMA, MACD, RSI, ATR, Volume ├─ On-chain data → OI, Funding Rate ├─ Quant data → Capital flow, OI changes (optional) └─ Recent trades → Last 10 closed trades ↓ 3. System Prompt (BuildSystemPrompt) ├─ Role definition ├─ Trading mode (aggressive/conservative/scalping) ├─ Hard constraints (code enforced) ├─ AI guidance (suggested values) ├─ Trading frequency ├─ Entry standards ├─ Decision process └─ Output format (XML + JSON) ↓ 4. User Prompt (BuildUserPrompt) ├─ System status (time, cycle number) ├─ BTC market overview ├─ Account information ├─ Current positions (with indicators) ├─ Candidate coins (full market data) └─ "Please analyze and output decisions..." ↓ 5. AI Request (CallWithMessages) ├─ Select AI model ├─ POST: system_prompt + user_prompt ├─ Timeout: 120s, Retries: 3 └─ Return raw response ↓ 6. AI Parsing (parseFullDecisionResponse) ├─ Extract Chain of Thought ├─ Extract JSON decision ├─ Fix character encoding ├─ Validate JSON format ├─ Parse decision array └─ Validate risk parameters ↓ 7. Decision Execution ├─ Sort: Close first → Open → hold/wait ├─ Risk control enforcement ├─ Submit orders ├─ Confirm fills └─ Record to database ``` --- ## 1. Coin Selection **Core File:** `decision/engine.go:380-454` **Entry Method:** `StrategyEngine.GetCandidateCoins()` ### 1.1 Static Coin List ```go // decision/engine.go:395-403 if config.CoinSource.SourceType == "static" { for _, symbol := range config.CoinSource.StaticCoins { coins = append(coins, CandidateCoin{ Symbol: market.Normalize(symbol), Sources: []string{"static"}, }) } } ``` - **Config:** `StrategyConfig.CoinSource.StaticCoins` - **Usage:** Manually specify trading coins - **Tag:** `["static"]` ### 1.2 AI500 Coin Pool ```go // decision/engine.go:405-406, 456-474 func (e *StrategyEngine) getCoinPoolCoins(limit int) []CandidateCoin { coins, err := e.provider.GetTopRatedCoins(limit) // ... for _, coin := range coins { result = append(result, CandidateCoin{ Symbol: coin.Symbol, Sources: []string{"ai500"}, }) } } ``` - **API:** `config.CoinSource.CoinPoolAPIURL` - **Usage:** Get top N coins by AI rating - **Tag:** `["ai500"]` ### 1.3 OI Top Coins (Position Growth Ranking) ```go // decision/engine.go:408-409, 476-498 func (e *StrategyEngine) getOITopCoins() []CandidateCoin { positions, err := e.provider.GetOITopPositions() // ... for _, pos := range positions { result = append(result, CandidateCoin{ Symbol: pos.Symbol, Sources: []string{"oi_top"}, }) } } ``` - **API:** `config.CoinSource.OITopAPIURL` - **Usage:** Get coins with fastest OI growth - **Tag:** `["oi_top"]` ### 1.4 Mixed Mode ```go // decision/engine.go:411-449 if config.CoinSource.SourceType == "mixed" { if config.CoinSource.UseCoinPool { // Add AI500 coins } if config.CoinSource.UseOITop { // Add OI Top coins } if len(config.CoinSource.StaticCoins) > 0 { // Add static coins } // Deduplicate and merge, keep multi-source tags } ``` - **Feature:** Use multiple data sources simultaneously - **Tag Example:** `["ai500", "oi_top"]` (dual signal coin) --- ## 2. Data Assembly **Core File:** `trader/auto_trader.go:562-791`, `decision/engine.go:299-374` **Entry Method:** `AutoTrader.buildTradingContext()` ### 2.1 Account Data ```go // trader/auto_trader.go:565-583 balance, err := at.trader.GetBalance() equity := balance["total_equity"].(float64) available := balance["available_balance"].(float64) unrealizedPnL := balance["total_pnl"].(float64) ``` **Extracted Fields:** - `total_equity` - Total account equity - `available_balance` - Available balance - `total_pnl` - Unrealized PnL ### 2.2 Position Data ```go // trader/auto_trader.go:588-682 positions, err := at.trader.GetPositions() for _, pos := range positions { position := decision.Position{ Symbol: pos.Symbol, Side: pos.Side, // "long" / "short" EntryPrice: pos.EntryPrice, MarkPrice: pos.MarkPrice, Quantity: pos.Quantity, Leverage: pos.Leverage, UnrealizedPnL: pos.UnrealizedPnL, LiquidationPrice: pos.LiquidationPrice, } } ``` ### 2.3 Market Data Fetching ```go // decision/engine.go:299-374 func (e *StrategyEngine) fetchMarketDataWithStrategy(symbols []string) map[string]*market.Data { timeframes := config.Indicators.Klines.SelectedTimeframes // ["5m", "15m", "1h", "4h"] primaryTF := config.Indicators.Klines.PrimaryTimeframe // "5m" count := config.Indicators.Klines.PrimaryCount // 30 for _, symbol := range symbols { data := market.GetWithTimeframes(symbol, timeframes, primaryTF, count) result[symbol] = data } } ``` ### 2.4 Technical Indicator Calculation **File:** `market/data.go:59-98` | Indicator | Config | Calculation | |-----------|--------|-------------| | **EMA** | `EnableEMA`, `EMAPeriods` | `calculateEMA(klines, period)` | | **MACD** | `EnableMACD` | `calculateMACD(klines)` - 12/26/9 | | **RSI** | `EnableRSI`, `RSIPeriods` | `calculateRSI(klines, period)` | | **ATR** | `EnableATR`, `ATRPeriods` | `calculateATR(klines, period)` | | **Volume** | `EnableVolume` | Raw volume data | | **OI** | `EnableOI` | Open interest data | | **Funding Rate** | `EnableFundingRate` | Funding rate | ### 2.5 Quant Data (Optional) ```go // trader/auto_trader.go:759-778 if config.Indicators.EnableQuantData { quantData := provider.GetQuantData(symbol) // Contains: Capital flow, OI changes, Price changes } ``` **Data Structure:** ```go QuantData { Netflow { Institution: {Future, Spot}, // Institutional flow Personal: {Future, Spot} // Retail flow }, OI { CurrentOI: float64, Delta: {1h, 4h, 24h} // OI changes }, PriceChange { "1h", "4h", "24h": float64 // Price change % } } ``` --- ## 3. System Prompt **Core File:** `decision/engine.go:700-818` **Entry Method:** `StrategyEngine.BuildSystemPrompt(accountEquity, variant)` ### 3.1 Prompt Structure (8 Sections) ``` 1. Role Definition [Editable] 2. Trading Mode Variant [Runtime determined] 3. Hard Constraints [Code enforced + AI guided] 4. Trading Frequency [Editable] 5. Entry Standards [Editable] 6. Decision Process [Editable] 7. Output Format [Fixed XML + JSON structure] 8. Custom Prompt [Optional] ``` ### 3.2 Role Definition ```go // decision/engine.go:706-713 roleDefinition := config.PromptSections.RoleDefinition if roleDefinition == "" { roleDefinition = "You are a professional cryptocurrency trading AI..." } ``` ### 3.3 Trading Mode Variants | Mode | Characteristics | |------|-----------------| | `aggressive` | Trend breakout, higher position tolerance | | `conservative` | Multi-signal confirmation, conservative money management | | `scalping` | Short-term momentum, tight take-profit | ### 3.4 Hard Constraints **Code Enforced:** ```go // decision/engine.go:725-749 maxPositions := config.RiskControl.MaxPositions // Default: 3 altcoinMaxRatio := config.RiskControl.AltcoinMaxPositionValueRatio // Default: 1.0 btcethMaxRatio := config.RiskControl.BTCETHMaxPositionValueRatio // Default: 5.0 maxMarginUsage := config.RiskControl.MaxMarginUsage // Default: 90% minPositionSize := config.RiskControl.MinPositionSize // Default: 12 USDT ``` **AI Guided (Suggested Values):** ```go altcoinMaxLeverage := config.RiskControl.AltcoinMaxLeverage // Default: 5x btcethMaxLeverage := config.RiskControl.BTCETHMaxLeverage // Default: 5x minRiskRewardRatio := config.RiskControl.MinRiskRewardRatio // Default: 1:3 minConfidence := config.RiskControl.MinConfidence // Default: 75 ``` ### 3.5 Output Format Requirements ```xml [Chain of Thought analysis process] ```json [ { "symbol": "BTCUSDT", "action": "open_long", "leverage": 5, "position_size_usd": 100.00, "stop_loss": 65000.00, "take_profit": 72000.00, "confidence": 85, "risk_usd": 20.00, "reasoning": "..." } ] ``` ``` --- ## 4. User Prompt **Core File:** `decision/engine.go:884-1007` **Entry Method:** `StrategyEngine.BuildUserPrompt(ctx)` ### 4.1 Prompt Content Structure ``` 1. System Status [Time, cycle number, runtime] 2. BTC Market Overview [Price, change%, MACD, RSI] 3. Account Info [Equity, balance%, PnL%, margin%, positions] 4. Recent Trades [Last 10 closed trades] 5. Current Positions [Detailed position data + indicators] 6. Candidate Coins [Full market data] 7. Quant Data [Capital flow, OI data] (optional) 8. OI Ranking Data [Market OI change ranking] (optional) ``` ### 4.2 Account Info Format ``` Account: Equity 1000.00 | Balance 800.00 (80.0%) | PnL +5.5% | Margin 20.0% | Positions 2 ``` ### 4.3 Position Info Format ``` 1. BTCUSDT LONG | Entry 68000.0000 Current 69500.0000 Qty 0.0100 | Position Value $695.00 PnL +2.21% | Amount +$15.00 Peak PnL +3.50% | Leverage 5x Margin $139.00 | Liquidation Price 55000.0000 Holding Duration 2 hours 30 minutes Market: price=69500, ema20=68800, macd=150.5, rsi7=62.3 OI: Latest=15000000, Avg=14500000 Funding Rate: 0.0100% ``` ### 4.4 Candidate Coin Format ``` ### 1. ETHUSDT (AI500+OI_Top dual signal) current_price = 3500.00, current_ema20 = 3450.00, current_macd = 25.5, current_rsi7 = 58.0 Open Interest: Latest: 8500000.00 Average: 8200000.00 Funding Rate: 0.0050 === 5M TIMEFRAME (oldest → latest) === Prices: [3480, 3485, 3490, 3495, 3500] Volumes: [1000, 1200, 1100, 1300, 1150] EMA20: [3470, 3475, 3478, 3482, 3485] MACD: [20.1, 21.5, 22.8, 24.0, 25.5] RSI7: [55.0, 56.2, 57.1, 57.8, 58.0] === 15M TIMEFRAME === ... ``` --- ## 5. AI Request **Core File:** `decision/engine.go:222-293`, `mcp/client.go:136-150` ### 5.1 Request Flow ```go // decision/engine.go:263-268 aiCallStart := time.Now() aiResponse, err := mcpClient.CallWithMessages(systemPrompt, userPrompt) aiCallDuration := time.Since(aiCallStart) ``` ### 5.2 Supported AI Models | Model | Client File | Default Model | |-------|-------------|---------------| | **DeepSeek** | `mcp/deepseek_client.go` | deepseek-chat | | **Qwen** | `mcp/qwen_client.go` | qwen-max | | **Claude** | `mcp/claude_client.go` | claude-3-5-sonnet | | **Gemini** | `mcp/gemini_client.go` | gemini-pro | | **Grok** | `mcp/grok_client.go` | grok-beta | | **OpenAI** | `mcp/openai_client.go` | gpt-5.2 | | **Kimi** | `mcp/kimi_client.go` | moonshot-v1-8k | ### 5.3 Request Parameters ```go // mcp/client.go Timeout: 120 seconds MaxRetries: 3 RetryDelay: 2 seconds (exponential backoff) ``` --- ## 6. AI Response Parsing **Core File:** `decision/engine.go:1303-1604` **Entry Method:** `parseFullDecisionResponse(response, accountEquity, leverage, ratio)` ### 6.1 Parsing Flow ``` Raw AI Response (text) ↓ 1. Extract Chain of Thought [extractCoTTrace()] ↓ 2. Extract JSON Decision [extractDecisions()] ↓ 3. Validate JSON Format [validateJSONFormat()] ↓ 4. Parse JSON [json.Unmarshal()] ↓ 5. Validate Decisions [validateDecisions()] ↓ 6. Build FullDecision [Return structured result] ``` ### 6.2 Chain of Thought Extraction ```go // decision/engine.go:1327-1345 func extractCoTTrace(response string) string { // Priority 1: XML tag if match := reReasoningTag.FindStringSubmatch(response); len(match) > 1 { return strings.TrimSpace(match[1]) } // Priority 2: Text before tag // Priority 3: Text before JSON [ // Priority 4: Full response } ``` ### 6.3 JSON Decision Extraction ```go // decision/engine.go:1347-1408 func extractDecisions(response string) (string, error) { // 1. Remove invisible characters response = removeInvisibleRunes(response) // 2. Fix character encoding response = fixMissingQuotes(response) // 3. Extract JSON (priority) // - XML tag + ```json // - Standalone ```json code block // - Bare JSON array } ``` ### 6.4 Character Encoding Fix ```go // decision/engine.go:1410-1432 func fixMissingQuotes(s string) string { // Chinese quotes → ASCII s = strings.ReplaceAll(s, """, "\"") s = strings.ReplaceAll(s, """, "\"") // Chinese brackets → ASCII s = strings.ReplaceAll(s, "[", "[") s = strings.ReplaceAll(s, "]", "]") s = strings.ReplaceAll(s, "{", "{") s = strings.ReplaceAll(s, "}", "}") // Chinese punctuation → ASCII s = strings.ReplaceAll(s, ":", ":") s = strings.ReplaceAll(s, ",", ",") } ``` ### 6.5 Decision Validation ```go // decision/engine.go:1480-1602 func validateDecisions(decisions []Decision, equity, leverage, ratio float64) error { for _, d := range decisions { // 1. Validate action type validActions := []string{"open_long", "open_short", "close_long", "close_short", "hold", "wait"} // 2. Open position validation if isOpenAction(d.Action) { // Leverage range check // Position size check // Stop loss/take profit check // Risk/reward ratio check // Confidence check } // 3. Close position validation if isCloseAction(d.Action) { // Symbol must exist } } } ``` ### 6.6 Decision Structure ```go // decision/engine.go:128-143 type Decision struct { Symbol string // Trading pair: "BTCUSDT" Action string // "open_long", "open_short", "close_long", "close_short", "hold", "wait" Leverage int // Leverage multiplier PositionSizeUSD float64 // Position value (USDT) StopLoss float64 // Stop loss price TakeProfit float64 // Take profit price Confidence int // Confidence 0-100 RiskUSD float64 // Max risk (USDT) Reasoning string // Decision reasoning } ``` --- ## 7. Decision Execution **Core File:** `trader/auto_trader.go:392-560` ### 7.1 Decision Sorting ```go // trader/auto_trader.go:519-526 sort.SliceStable(decisions, func(i, j int) bool { priority := map[string]int{ "close_long": 1, "close_short": 1, // Highest priority "open_long": 2, "open_short": 2, // Second priority "hold": 3, "wait": 3, // Lowest priority } return priority[decisions[i].Action] < priority[decisions[j].Action] }) ``` ### 7.2 Risk Control Enforcement **File:** `trader/auto_trader.go:1769-1851` | Check | Method | Action | |-------|--------|--------| | Max positions | `enforceMaxPositions()` | Reject new opens | | Position value cap | `enforcePositionValueRatio()` | Auto reduce size | | Min position | `enforceMinPositionSize()` | Reject small orders | | Margin adjustment | Auto calculate | Adjust by available balance | ### 7.3 Order Execution ```go // trader/auto_trader.go:1631-1767 func (at *AutoTrader) recordAndConfirmOrder(orderID, symbol, side, action string) { // 1. Poll order status (5 retries, 500ms interval) for i := 0; i < 5; i++ { status := at.trader.GetOrderStatus(orderID) if status.Status == "FILLED" { break } time.Sleep(500 * time.Millisecond) } // 2. Extract fill info filledPrice := status.AvgPrice filledQty := status.FilledQty fee := status.Fee // 3. Record to database at.store.Position().SaveOrder(...) } ``` ### 7.4 Decision Log Saving ```go // trader/auto_trader.go:1235-1256 record := &store.DecisionRecord{ CycleNumber: cycleNumber, TraderID: traderID, Timestamp: time.Now(), SystemPrompt: systemPrompt, // Full system prompt InputPrompt: userPrompt, // Full user prompt CoTTrace: cotTrace, // AI chain of thought DecisionJSON: decisionsJSON, // Parsed decisions RawResponse: rawResponse, // Raw AI response ExecutionLog: executionResults, // Execution results CandidateCoins: candidateCoins, // Candidate coins Success: success, // Execution status } at.store.Decision().LogDecision(record) ``` --- ## Core File Index | Module | File | Key Methods | |--------|------|-------------| | **Main Loop** | `trader/auto_trader.go` | `Run()`, `runCycle()`, `buildTradingContext()` | | **Coin Selection** | `decision/engine.go:380-454` | `GetCandidateCoins()` | | **Data Fetching** | `market/data.go` | `Get()`, `GetWithTimeframes()` | | **Indicator Calc** | `market/data.go:59-98` | `calculateEMA()`, `calculateMACD()`, `calculateRSI()` | | **System Prompt** | `decision/engine.go:700-818` | `BuildSystemPrompt()` | | **User Prompt** | `decision/engine.go:884-1007` | `BuildUserPrompt()` | | **Market Format** | `decision/engine.go:1029-1099` | `formatMarketData()` | | **AI Request** | `decision/engine.go:222-293` | `GetFullDecisionWithStrategy()` | | **MCP Client** | `mcp/client.go:136-150` | `CallWithMessages()` | | **Response Parse** | `decision/engine.go:1303-1604` | `parseFullDecisionResponse()` | | **CoT Extract** | `decision/engine.go:1327-1345` | `extractCoTTrace()` | | **JSON Extract** | `decision/engine.go:1347-1408` | `extractDecisions()` | | **Decision Valid** | `decision/engine.go:1480-1602` | `validateDecisions()` | | **Risk Enforce** | `trader/auto_trader.go:1769-1851` | `enforceMaxPositions()`, `enforcePositionValueRatio()` | | **Strategy Config** | `store/strategy.go` | `StrategyConfig`, `RiskControlConfig` | | **Data Provider** | `provider/data_provider.go` | `GetAI500Data()`, `GetOITopPositions()` | --- ## Configuration Reference ### Strategy Config Structure ```go // store/strategy.go type StrategyConfig struct { // Coin Source CoinSource struct { SourceType string // "static", "coinpool", "oi_top", "mixed" StaticCoins []string // Static coin list UseCoinPool bool // Use AI500 UseOITop bool // Use OI ranking CoinPoolLimit int // AI500 fetch limit CoinPoolAPIURL string // AI500 API URL OITopAPIURL string // OI ranking API URL } // Technical Indicators Indicators struct { EnableEMA bool EMAPeriods []int // [20, 50] EnableMACD bool EnableRSI bool RSIPeriods []int // [7, 14] EnableATR bool ATRPeriods []int // [14] EnableVolume bool EnableOI bool EnableFundingRate bool EnableQuantData bool EnableOIRanking bool Klines struct { PrimaryTimeframe string // "5m" SelectedTimeframes []string // ["5m", "15m", "1h", "4h"] PrimaryCount int // 30 } } // Risk Control RiskControl struct { MaxPositions int // Max positions BTCETHMaxLeverage int // BTC/ETH max leverage AltcoinMaxLeverage int // Altcoin max leverage BTCETHMaxPositionValueRatio float64 // BTC/ETH position ratio cap AltcoinMaxPositionValueRatio float64 // Altcoin position ratio cap MaxMarginUsage float64 // Max margin usage MinPositionSize float64 // Min position size MinRiskRewardRatio float64 // Min risk/reward ratio MinConfidence int // Min confidence } // Prompt Sections PromptSections struct { RoleDefinition string TradingFrequency string EntryStandards string DecisionProcess string } // Custom Prompt CustomPrompt string } ``` --- **Document Version:** 1.0.0 **Last Updated:** 2025-01-15