Files
nofx/assistant/strategy_builder.go
tinklefund 8b4ce279da feat: Add powerful flexible strategy system
Strategy Builder:
- Create strategies from natural language
- Grid trading strategy
- DCA (Dollar Cost Averaging) strategy
- Trend following (EMA crossover) strategy
- Custom rule-based strategies

Strategy Components:
- Entry/exit rules with indicators (RSI, EMA, MACD, etc.)
- Position sizing (fixed, percent, risk-based, kelly)
- Risk management (max drawdown, daily loss limit, cooldown)
- Leverage config (fixed, dynamic, per-symbol, per-volatility)
- Time-based rules (trading hours, hold time limits)
- AI enhancement (confidence threshold, personality)

11 New Tools:
- create_strategy - Natural language strategy creation
- create_grid_strategy - Grid trading setup
- create_dca_strategy - DCA setup
- create_trend_strategy - Trend following setup
- list_smart_strategies - List all strategies
- get_strategy_details - Strategy details
- update_strategy - Modify strategy settings
- activate_strategy - Start trading
- deactivate_strategy - Stop trading
- delete_strategy - Remove strategy
- get_strategy_templates - Show available templates

Total tools now: 24 (13 trading + 11 strategy)
2026-01-30 03:45:10 +08:00

383 lines
13 KiB
Go

// Package assistant - Intelligent Strategy Builder
// Allows users to create powerful, flexible trading strategies through natural language
package assistant
import (
"fmt"
"nofx/store"
"strings"
"time"
"github.com/google/uuid"
)
// StrategyType defines the type of trading strategy
type StrategyType string
const (
StrategyTypeAI StrategyType = "ai" // AI decides everything
StrategyTypeTrend StrategyType = "trend" // Trend following
StrategyTypeMeanRevert StrategyType = "mean_revert" // Mean reversion
StrategyTypeGrid StrategyType = "grid" // Grid trading
StrategyTypeDCA StrategyType = "dca" // Dollar cost averaging
StrategyTypeBreakout StrategyType = "breakout" // Breakout trading
StrategyTypeArbitrage StrategyType = "arbitrage" // Cross-exchange arbitrage
StrategyTypeCustom StrategyType = "custom" // Custom rules
)
// SmartStrategy represents a user-defined trading strategy
type SmartStrategy struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type StrategyType `json:"type"`
// Trading pairs
Symbols []string `json:"symbols"` // e.g., ["BTCUSDT", "ETHUSDT"]
SymbolMode string `json:"symbol_mode"` // "static", "ai_select", "top_volume", "top_oi"
MaxSymbols int `json:"max_symbols"` // Max symbols to trade simultaneously
// Entry conditions
EntryRules []Rule `json:"entry_rules"`
EntryMode string `json:"entry_mode"` // "any" (OR) or "all" (AND)
// Exit conditions
ExitRules []Rule `json:"exit_rules"`
TakeProfit *float64 `json:"take_profit"` // TP percentage
StopLoss *float64 `json:"stop_loss"` // SL percentage
TrailingStop *float64 `json:"trailing_stop"` // Trailing stop percentage
// Position sizing
PositionSize PositionSizeConfig `json:"position_size"`
MaxPositions int `json:"max_positions"` // Max concurrent positions
MaxPerSymbol int `json:"max_per_symbol"` // Max positions per symbol
// Risk management
RiskConfig RiskConfig `json:"risk_config"`
// Leverage settings
LeverageConfig LeverageConfig `json:"leverage_config"`
// Time settings
TimeConfig TimeConfig `json:"time_config"`
// AI enhancement
AIConfig AIStrategyConfig `json:"ai_config"`
// Metadata
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy string `json:"created_by"`
IsActive bool `json:"is_active"`
Performance *StrategyPerformance `json:"performance,omitempty"`
}
// Rule represents a trading rule/condition
type Rule struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"` // "indicator", "price", "time", "volume", "ai", "custom"
Indicator string `json:"indicator"` // e.g., "RSI", "MACD", "EMA"
Condition string `json:"condition"` // e.g., "crosses_above", "greater_than", "less_than"
Value interface{} `json:"value"` // The value to compare against
Timeframe string `json:"timeframe"` // e.g., "1h", "4h", "1d"
Weight float64 `json:"weight"` // Weight for scoring (0-1)
Description string `json:"description"` // Human readable description
}
// PositionSizeConfig defines how to size positions
type PositionSizeConfig struct {
Mode string `json:"mode"` // "fixed", "percent", "risk_based", "kelly"
FixedAmount float64 `json:"fixed_amount"` // Fixed USDT amount
PercentOfEquity float64 `json:"percent_of_equity"` // Percentage of total equity
RiskPerTrade float64 `json:"risk_per_trade"` // Max risk per trade (%)
MaxSingleTrade float64 `json:"max_single_trade"` // Max single trade size (USDT)
}
// RiskConfig defines risk management rules
type RiskConfig struct {
MaxDrawdown float64 `json:"max_drawdown"` // Max drawdown before stopping (%)
MaxDailyLoss float64 `json:"max_daily_loss"` // Max daily loss (%)
MaxOpenRisk float64 `json:"max_open_risk"` // Max total open risk (%)
CooldownAfterLoss int `json:"cooldown_after_loss"` // Minutes to wait after a loss
RequireConfirmation bool `json:"require_confirmation"` // Require user confirmation for trades
EmergencyStopLoss float64 `json:"emergency_stop_loss"` // Emergency SL for all positions (%)
}
// LeverageConfig defines leverage settings
type LeverageConfig struct {
Mode string `json:"mode"` // "fixed", "dynamic", "per_symbol"
DefaultLeverage int `json:"default_leverage"`
MaxLeverage int `json:"max_leverage"`
PerSymbol map[string]int `json:"per_symbol"` // Symbol-specific leverage
PerVolatility []VolatilityLever `json:"per_volatility"` // Volatility-based leverage
}
// VolatilityLever defines leverage based on volatility
type VolatilityLever struct {
MaxVolatility float64 `json:"max_volatility"` // ATR percentage threshold
Leverage int `json:"leverage"`
}
// TimeConfig defines time-based settings
type TimeConfig struct {
TradingHours []TimeRange `json:"trading_hours"` // When to trade
AvoidNews bool `json:"avoid_news"` // Avoid major news events
AvoidWeekends bool `json:"avoid_weekends"`
MinHoldTime int `json:"min_hold_time"` // Minimum hold time (minutes)
MaxHoldTime int `json:"max_hold_time"` // Maximum hold time (minutes)
ScanInterval int `json:"scan_interval"` // How often to scan (minutes)
}
// TimeRange represents a time range
type TimeRange struct {
Start string `json:"start"` // "09:00"
End string `json:"end"` // "17:00"
TZ string `json:"tz"` // Timezone
}
// AIStrategyConfig defines AI-specific settings
type AIStrategyConfig struct {
Enabled bool `json:"enabled"`
Model string `json:"model"` // AI model to use
ConfidenceThreshold float64 `json:"confidence_threshold"` // Min confidence to act
UseMarketSentiment bool `json:"use_market_sentiment"`
UseTechnicalAnalysis bool `json:"use_technical_analysis"`
UseOnChainData bool `json:"use_onchain_data"`
CustomPrompt string `json:"custom_prompt"` // Custom instructions for AI
Personality string `json:"personality"` // "aggressive", "conservative", "balanced"
}
// StrategyPerformance tracks strategy performance
type StrategyPerformance struct {
TotalTrades int `json:"total_trades"`
WinningTrades int `json:"winning_trades"`
LosingTrades int `json:"losing_trades"`
WinRate float64 `json:"win_rate"`
TotalPnL float64 `json:"total_pnl"`
MaxDrawdown float64 `json:"max_drawdown"`
SharpeRatio float64 `json:"sharpe_ratio"`
ProfitFactor float64 `json:"profit_factor"`
AvgWin float64 `json:"avg_win"`
AvgLoss float64 `json:"avg_loss"`
LastUpdated time.Time `json:"last_updated"`
}
// StrategyBuilder helps users create strategies through conversation
type StrategyBuilder struct {
store *store.Store
}
// NewStrategyBuilder creates a new strategy builder
func NewStrategyBuilder(st *store.Store) *StrategyBuilder {
return &StrategyBuilder{store: st}
}
// CreateFromNaturalLanguage creates a strategy from natural language description
func (sb *StrategyBuilder) CreateFromNaturalLanguage(description string, userID string) (*SmartStrategy, error) {
// This would typically call an AI to parse the description
// For now, we create a basic template
strategy := &SmartStrategy{
ID: uuid.New().String()[:8],
Name: "Custom Strategy",
Description: description,
Type: StrategyTypeAI,
SymbolMode: "ai_select",
MaxSymbols: 5,
EntryMode: "all",
MaxPositions: 5,
MaxPerSymbol: 1,
PositionSize: PositionSizeConfig{
Mode: "percent",
PercentOfEquity: 5,
MaxSingleTrade: 1000,
},
RiskConfig: RiskConfig{
MaxDrawdown: 20,
MaxDailyLoss: 5,
MaxOpenRisk: 10,
CooldownAfterLoss: 30,
RequireConfirmation: true,
EmergencyStopLoss: 30,
},
LeverageConfig: LeverageConfig{
Mode: "dynamic",
DefaultLeverage: 3,
MaxLeverage: 10,
},
TimeConfig: TimeConfig{
ScanInterval: 5,
AvoidWeekends: false,
},
AIConfig: AIStrategyConfig{
Enabled: true,
ConfidenceThreshold: 0.7,
UseMarketSentiment: true,
UseTechnicalAnalysis: true,
Personality: "balanced",
CustomPrompt: description,
},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
CreatedBy: userID,
IsActive: false,
}
return strategy, nil
}
// CreateGridStrategy creates a grid trading strategy
func (sb *StrategyBuilder) CreateGridStrategy(symbol string, lowerPrice, upperPrice float64, gridCount int, amountPerGrid float64) *SmartStrategy {
return &SmartStrategy{
ID: uuid.New().String()[:8],
Name: fmt.Sprintf("Grid %s", symbol),
Description: fmt.Sprintf("Grid trading %s from %.2f to %.2f with %d grids", symbol, lowerPrice, upperPrice, gridCount),
Type: StrategyTypeGrid,
Symbols: []string{symbol},
SymbolMode: "static",
MaxPositions: gridCount,
PositionSize: PositionSizeConfig{
Mode: "fixed",
FixedAmount: amountPerGrid,
},
EntryRules: []Rule{
{
ID: "grid_entry",
Type: "price",
Condition: "grid_level",
Value: map[string]interface{}{
"lower_price": lowerPrice,
"upper_price": upperPrice,
"grid_count": gridCount,
},
},
},
CreatedAt: time.Now(),
IsActive: false,
}
}
// CreateDCAStrategy creates a DCA strategy
func (sb *StrategyBuilder) CreateDCAStrategy(symbol string, intervalMinutes int, amountPerBuy float64, maxBuys int) *SmartStrategy {
return &SmartStrategy{
ID: uuid.New().String()[:8],
Name: fmt.Sprintf("DCA %s", symbol),
Description: fmt.Sprintf("DCA into %s every %d minutes, $%.2f per buy, max %d buys", symbol, intervalMinutes, amountPerBuy, maxBuys),
Type: StrategyTypeDCA,
Symbols: []string{symbol},
SymbolMode: "static",
MaxPositions: maxBuys,
PositionSize: PositionSizeConfig{
Mode: "fixed",
FixedAmount: amountPerBuy,
},
TimeConfig: TimeConfig{
ScanInterval: intervalMinutes,
},
CreatedAt: time.Now(),
IsActive: false,
}
}
// CreateTrendStrategy creates a trend following strategy
func (sb *StrategyBuilder) CreateTrendStrategy(symbols []string, emaFast, emaSlow int, leverage int) *SmartStrategy {
return &SmartStrategy{
ID: uuid.New().String()[:8],
Name: "Trend Following",
Description: fmt.Sprintf("EMA %d/%d crossover strategy", emaFast, emaSlow),
Type: StrategyTypeTrend,
Symbols: symbols,
SymbolMode: "static",
EntryMode: "all",
EntryRules: []Rule{
{
ID: "ema_cross",
Name: "EMA Crossover",
Type: "indicator",
Indicator: "EMA",
Condition: "crosses_above",
Value: map[string]int{
"fast_period": emaFast,
"slow_period": emaSlow,
},
Timeframe: "1h",
Weight: 1.0,
},
},
ExitRules: []Rule{
{
ID: "ema_cross_exit",
Name: "EMA Crossover Exit",
Type: "indicator",
Indicator: "EMA",
Condition: "crosses_below",
Value: map[string]int{
"fast_period": emaFast,
"slow_period": emaSlow,
},
Timeframe: "1h",
},
},
LeverageConfig: LeverageConfig{
Mode: "fixed",
DefaultLeverage: leverage,
},
CreatedAt: time.Now(),
IsActive: false,
}
}
// StrategyToPrompt converts a strategy to an AI prompt
func StrategyToPrompt(s *SmartStrategy) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("# 策略: %s\n\n", s.Name))
sb.WriteString(fmt.Sprintf("**描述**: %s\n", s.Description))
sb.WriteString(fmt.Sprintf("**类型**: %s\n\n", s.Type))
// Trading pairs
if len(s.Symbols) > 0 {
sb.WriteString(fmt.Sprintf("**交易对**: %s\n", strings.Join(s.Symbols, ", ")))
} else {
sb.WriteString(fmt.Sprintf("**选币模式**: %s (最多 %d 个)\n", s.SymbolMode, s.MaxSymbols))
}
// Entry rules
if len(s.EntryRules) > 0 {
sb.WriteString("\n## 入场规则\n")
for _, rule := range s.EntryRules {
sb.WriteString(fmt.Sprintf("- %s: %s %s %v\n", rule.Name, rule.Indicator, rule.Condition, rule.Value))
}
}
// Exit rules
sb.WriteString("\n## 出场规则\n")
if s.TakeProfit != nil {
sb.WriteString(fmt.Sprintf("- 止盈: %.1f%%\n", *s.TakeProfit))
}
if s.StopLoss != nil {
sb.WriteString(fmt.Sprintf("- 止损: %.1f%%\n", *s.StopLoss))
}
if s.TrailingStop != nil {
sb.WriteString(fmt.Sprintf("- 移动止损: %.1f%%\n", *s.TrailingStop))
}
// Risk management
sb.WriteString("\n## 风险管理\n")
sb.WriteString(fmt.Sprintf("- 最大回撤: %.1f%%\n", s.RiskConfig.MaxDrawdown))
sb.WriteString(fmt.Sprintf("- 单日最大亏损: %.1f%%\n", s.RiskConfig.MaxDailyLoss))
sb.WriteString(fmt.Sprintf("- 最大持仓数: %d\n", s.MaxPositions))
// AI settings
if s.AIConfig.Enabled {
sb.WriteString("\n## AI 配置\n")
sb.WriteString(fmt.Sprintf("- 置信度阈值: %.0f%%\n", s.AIConfig.ConfidenceThreshold*100))
sb.WriteString(fmt.Sprintf("- 风格: %s\n", s.AIConfig.Personality))
if s.AIConfig.CustomPrompt != "" {
sb.WriteString(fmt.Sprintf("- 自定义指令: %s\n", s.AIConfig.CustomPrompt))
}
}
return sb.String()
}