diff --git a/api/strategy.go b/api/strategy.go index cadb82fb..d96b3826 100644 --- a/api/strategy.go +++ b/api/strategy.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" "net/http" - "nofx/decision" + "nofx/kernel" "nofx/logger" "nofx/market" "nofx/mcp" @@ -397,7 +397,7 @@ func (s *Server) handlePreviewPrompt(c *gin.Context) { } // Create strategy engine to build prompt - engine := decision.NewStrategyEngine(&req.Config) + engine := kernel.NewStrategyEngine(&req.Config) // Build system prompt (using built-in method from strategy engine) systemPrompt := engine.BuildSystemPrompt( @@ -443,7 +443,7 @@ func (s *Server) handleStrategyTestRun(c *gin.Context) { } // Create strategy engine to build prompt - engine := decision.NewStrategyEngine(&req.Config) + engine := kernel.NewStrategyEngine(&req.Config) // Get candidate coins candidates, err := engine.GetCandidateCoins() @@ -505,11 +505,11 @@ func (s *Server) handleStrategyTestRun(c *gin.Context) { oiRankingData := engine.FetchOIRankingData() // Build real context (for generating User Prompt) - testContext := &decision.Context{ + testContext := &kernel.Context{ CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"), RuntimeMinutes: 0, CallCount: 1, - Account: decision.AccountInfo{ + Account: kernel.AccountInfo{ TotalEquity: 1000.0, AvailableBalance: 1000.0, UnrealizedPnL: 0, @@ -519,7 +519,7 @@ func (s *Server) handleStrategyTestRun(c *gin.Context) { MarginUsedPct: 0, PositionCount: 0, }, - Positions: []decision.PositionInfo{}, + Positions: []kernel.PositionInfo{}, CandidateCoins: candidates, PromptVariant: req.PromptVariant, MarketDataMap: marketDataMap, diff --git a/backtest/aicache.go b/backtest/aicache.go index 7d4f7168..f5627e92 100644 --- a/backtest/aicache.go +++ b/backtest/aicache.go @@ -9,7 +9,7 @@ import ( "path/filepath" "sync" - "nofx/decision" + "nofx/kernel" "nofx/market" ) @@ -17,7 +17,7 @@ type cachedDecision struct { Key string `json:"key"` PromptVariant string `json:"prompt_variant"` Timestamp int64 `json:"ts"` - Decision *decision.FullDecision `json:"decision"` + Decision *kernel.FullDecision `json:"decision"` } // AICache persists AI decisions for repeated backtesting or replay. @@ -67,7 +67,7 @@ func (c *AICache) Path() string { return c.path } -func (c *AICache) Get(key string) (*decision.FullDecision, bool) { +func (c *AICache) Get(key string) (*kernel.FullDecision, bool) { if c == nil || key == "" { return nil, false } @@ -80,7 +80,7 @@ func (c *AICache) Get(key string) (*decision.FullDecision, bool) { return cloneDecision(entry.Decision), true } -func (c *AICache) Put(key string, variant string, ts int64, decision *decision.FullDecision) error { +func (c *AICache) Put(key string, variant string, ts int64, decision *kernel.FullDecision) error { if c == nil || key == "" || decision == nil { return nil } @@ -109,7 +109,7 @@ func (c *AICache) save() error { return writeFileAtomic(c.path, data, 0o644) } -func cloneDecision(src *decision.FullDecision) *decision.FullDecision { +func cloneDecision(src *kernel.FullDecision) *kernel.FullDecision { if src == nil { return nil } @@ -117,14 +117,14 @@ func cloneDecision(src *decision.FullDecision) *decision.FullDecision { if err != nil { return nil } - var dst decision.FullDecision + var dst kernel.FullDecision if err := json.Unmarshal(data, &dst); err != nil { return nil } return &dst } -func computeCacheKey(ctx *decision.Context, variant string, ts int64) (string, error) { +func computeCacheKey(ctx *kernel.Context, variant string, ts int64) (string, error) { if ctx == nil { return "", fmt.Errorf("context is nil") } @@ -132,9 +132,9 @@ func computeCacheKey(ctx *decision.Context, variant string, ts int64) (string, e Variant string `json:"variant"` Timestamp int64 `json:"ts"` CurrentTime string `json:"current_time"` - Account decision.AccountInfo `json:"account"` - Positions []decision.PositionInfo `json:"positions"` - CandidateCoins []decision.CandidateCoin `json:"candidate_coins"` + Account kernel.AccountInfo `json:"account"` + Positions []kernel.PositionInfo `json:"positions"` + CandidateCoins []kernel.CandidateCoin `json:"candidate_coins"` MarketData map[string]market.Data `json:"market"` MarginUsedPct float64 `json:"margin_used_pct"` Runtime int `json:"runtime_minutes"` diff --git a/backtest/runner.go b/backtest/runner.go index 8820d994..d40b236a 100644 --- a/backtest/runner.go +++ b/backtest/runner.go @@ -13,7 +13,7 @@ import ( "sync" "time" - "nofx/decision" + "nofx/kernel" "nofx/market" "nofx/mcp" "nofx/store" @@ -34,7 +34,7 @@ type Runner struct { cfg BacktestConfig feed *DataFeed account *BacktestAccount - strategyEngine *decision.StrategyEngine + strategyEngine *kernel.StrategyEngine decisionLogDir string mcpClient mcp.AIClient @@ -118,7 +118,7 @@ func NewRunner(cfg BacktestConfig, mcpClient mcp.AIClient) (*Runner, error) { // Create strategy engine from backtest config for unified prompt generation strategyConfig := cfg.ToStrategyConfig() - strategyEngine := decision.NewStrategyEngine(strategyConfig) + strategyEngine := kernel.NewStrategyEngine(strategyConfig) r := &Runner{ cfg: cfg, @@ -305,7 +305,7 @@ func (r *Runner) stepOnce() error { record = rec var ( - fullDecision *decision.FullDecision + fullDecision *kernel.FullDecision fromCache bool cacheKey string ) @@ -470,7 +470,7 @@ func (r *Runner) stepOnce() error { return nil } -func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Data, multiTF map[string]map[string]*market.Data, priceMap map[string]float64, callCount int) (*decision.Context, *store.DecisionRecord, error) { +func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Data, multiTF map[string]map[string]*market.Data, priceMap map[string]float64, callCount int) (*kernel.Context, *store.DecisionRecord, error) { equity, unrealized, _ := r.account.TotalEquity(priceMap) available := r.account.Cash() marginUsed := r.totalMarginUsed() @@ -479,7 +479,7 @@ func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Da marginPct = (marginUsed / equity) * 100 } - accountInfo := decision.AccountInfo{ + accountInfo := kernel.AccountInfo{ TotalEquity: equity, AvailableBalance: available, TotalPnL: equity - r.account.InitialBalance(), @@ -495,14 +495,14 @@ func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Da candidateCoins, err := r.strategyEngine.GetCandidateCoins() if err != nil { // Fallback to simple list if strategy engine fails - candidateCoins = make([]decision.CandidateCoin, 0, len(r.cfg.Symbols)) + candidateCoins = make([]kernel.CandidateCoin, 0, len(r.cfg.Symbols)) for _, sym := range r.cfg.Symbols { - candidateCoins = append(candidateCoins, decision.CandidateCoin{Symbol: sym, Sources: []string{"backtest"}}) + candidateCoins = append(candidateCoins, kernel.CandidateCoin{Symbol: sym, Sources: []string{"backtest"}}) } } runtime := int((ts - int64(r.cfg.StartTS*1000)) / 60000) - ctx := &decision.Context{ + ctx := &kernel.Context{ CurrentTime: time.UnixMilli(ts).UTC().Format("2006-01-02 15:04:05 UTC"), RuntimeMinutes: runtime, CallCount: callCount, @@ -566,7 +566,7 @@ func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Da return ctx, record, nil } -func (r *Runner) fillDecisionRecord(record *store.DecisionRecord, full *decision.FullDecision) { +func (r *Runner) fillDecisionRecord(record *store.DecisionRecord, full *kernel.FullDecision) { record.InputPrompt = full.UserPrompt record.CoTTrace = full.CoTTrace if len(full.Decisions) > 0 { @@ -576,12 +576,12 @@ func (r *Runner) fillDecisionRecord(record *store.DecisionRecord, full *decision } } -func (r *Runner) invokeAIWithRetry(ctx *decision.Context) (*decision.FullDecision, error) { +func (r *Runner) invokeAIWithRetry(ctx *kernel.Context) (*kernel.FullDecision, error) { var lastErr error for attempt := 0; attempt < aiDecisionMaxRetries; attempt++ { // Use GetFullDecisionWithStrategy with the pre-configured strategy engine // This ensures backtest uses the same unified prompt generation as live trading - fd, err := decision.GetFullDecisionWithStrategy( + fd, err := kernel.GetFullDecisionWithStrategy( ctx, r.mcpClient, r.strategyEngine, @@ -597,7 +597,7 @@ func (r *Runner) invokeAIWithRetry(ctx *decision.Context) (*decision.FullDecisio return nil, lastErr } -func (r *Runner) executeDecision(dec decision.Decision, priceMap map[string]float64, ts int64, cycle int) (store.DecisionAction, []TradeEvent, string, error) { +func (r *Runner) executeDecision(dec kernel.Decision, priceMap map[string]float64, ts int64, cycle int) (store.DecisionAction, []TradeEvent, string, error) { symbol := dec.Symbol usedLeverage := r.resolveLeverage(dec.Leverage, symbol) actionRecord := store.DecisionAction{ @@ -739,7 +739,7 @@ func (r *Runner) executeDecision(dec decision.Decision, priceMap map[string]floa } } -func (r *Runner) determineQuantity(dec decision.Decision, price float64) float64 { +func (r *Runner) determineQuantity(dec kernel.Decision, price float64) float64 { snapshot := r.snapshotState() equity := snapshot.Equity if equity <= 0 { @@ -777,7 +777,7 @@ func (r *Runner) determineQuantity(dec decision.Decision, price float64) float64 return qty } -func (r *Runner) determineCloseQuantity(symbol, side string, dec decision.Decision) float64 { +func (r *Runner) determineCloseQuantity(symbol, side string, dec kernel.Decision) float64 { for _, pos := range r.account.Positions() { if pos.Symbol == strings.ToUpper(symbol) && pos.Side == side { return pos.Quantity @@ -831,12 +831,12 @@ func (r *Runner) snapshotPositions(priceMap map[string]float64) []store.Position return list } -func (r *Runner) convertPositions(priceMap map[string]float64) []decision.PositionInfo { +func (r *Runner) convertPositions(priceMap map[string]float64) []kernel.PositionInfo { positions := r.account.Positions() - list := make([]decision.PositionInfo, 0, len(positions)) + list := make([]kernel.PositionInfo, 0, len(positions)) for _, pos := range positions { price := priceMap[pos.Symbol] - list = append(list, decision.PositionInfo{ + list = append(list, kernel.PositionInfo{ Symbol: pos.Symbol, Side: pos.Side, EntryPrice: pos.EntryPrice, @@ -1416,7 +1416,7 @@ func snapshotsToMap(snaps []PositionSnapshot) map[string]PositionSnapshot { return positions } -func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision { +func sortDecisionsByPriority(decisions []kernel.Decision) []kernel.Decision { if len(decisions) <= 1 { return decisions } @@ -1434,7 +1434,7 @@ func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision } } - result := make([]decision.Decision, len(decisions)) + result := make([]kernel.Decision, len(decisions)) copy(result, decisions) sort.Slice(result, func(i, j int) bool { diff --git a/debate/engine.go b/debate/engine.go index ca4913f9..06247e46 100644 --- a/debate/engine.go +++ b/debate/engine.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "nofx/decision" + "nofx/kernel" "nofx/logger" "nofx/market" "nofx/mcp" @@ -18,7 +18,7 @@ import ( // TraderExecutor interface for executing trades type TraderExecutor interface { - ExecuteDecision(decision *decision.Decision) error + ExecuteDecision(decision *kernel.Decision) error GetBalance() (map[string]interface{}, error) } @@ -166,7 +166,7 @@ func (e *DebateEngine) runDebate(session *store.DebateSessionWithDetails, strate }() // Create strategy engine for building context - strategyEngine := decision.NewStrategyEngine(strategyConfig) + strategyEngine := kernel.NewStrategyEngine(strategyConfig) // Build market context using strategy config ctx, err := e.buildMarketContext(session, strategyEngine) @@ -289,7 +289,7 @@ func (e *DebateEngine) runDebate(session *store.DebateSessionWithDetails, strate } // buildMarketContext builds the market context using strategy engine -func (e *DebateEngine) buildMarketContext(session *store.DebateSessionWithDetails, strategyEngine *decision.StrategyEngine) (*decision.Context, error) { +func (e *DebateEngine) buildMarketContext(session *store.DebateSessionWithDetails, strategyEngine *kernel.StrategyEngine) (*kernel.Context, error) { config := strategyEngine.GetConfig() // Get candidate coins @@ -336,11 +336,11 @@ func (e *DebateEngine) buildMarketContext(session *store.DebateSessionWithDetail oiRankingData := strategyEngine.FetchOIRankingData() // Build context - ctx := &decision.Context{ + ctx := &kernel.Context{ CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"), RuntimeMinutes: 0, CallCount: 1, - Account: decision.AccountInfo{ + Account: kernel.AccountInfo{ TotalEquity: 1000.0, // Simulated for debate AvailableBalance: 1000.0, UnrealizedPnL: 0, @@ -350,7 +350,7 @@ func (e *DebateEngine) buildMarketContext(session *store.DebateSessionWithDetail MarginUsedPct: 0, PositionCount: 0, }, - Positions: []decision.PositionInfo{}, + Positions: []kernel.PositionInfo{}, CandidateCoins: candidates, PromptVariant: session.PromptVariant, MarketDataMap: marketDataMap, @@ -539,7 +539,7 @@ func (e *DebateEngine) getParticipantResponse( } // collectVotes collects final votes from all participants -func (e *DebateEngine) collectVotes(session *store.DebateSessionWithDetails, strategyEngine *decision.StrategyEngine, allMessages []*store.DebateMessage) ([]*store.DebateVote, error) { +func (e *DebateEngine) collectVotes(session *store.DebateSessionWithDetails, strategyEngine *kernel.StrategyEngine, allMessages []*store.DebateMessage) ([]*store.DebateVote, error) { var votes []*store.DebateVote // Build voting context @@ -1009,7 +1009,7 @@ func (e *DebateEngine) ExecuteConsensus(sessionID string, executor TraderExecuto } // Create decision - tradeDecision := &decision.Decision{ + tradeDecision := &kernel.Decision{ Symbol: session.Symbol, Action: action, Leverage: session.FinalDecision.Leverage, diff --git a/decision/engine.go b/kernel/engine.go similarity index 99% rename from decision/engine.go rename to kernel/engine.go index ce29222f..f2dd927c 100644 --- a/decision/engine.go +++ b/kernel/engine.go @@ -1,4 +1,4 @@ -package decision +package kernel import ( "encoding/json" diff --git a/decision/formatter.go b/kernel/formatter.go similarity index 99% rename from decision/formatter.go rename to kernel/formatter.go index 9b2174bd..7a5593ca 100644 --- a/decision/formatter.go +++ b/kernel/formatter.go @@ -1,4 +1,4 @@ -package decision +package kernel import ( "fmt" diff --git a/decision/prompt_builder.go b/kernel/prompt_builder.go similarity index 99% rename from decision/prompt_builder.go rename to kernel/prompt_builder.go index 0c74c23c..dbeaa257 100644 --- a/decision/prompt_builder.go +++ b/kernel/prompt_builder.go @@ -1,4 +1,4 @@ -package decision +package kernel import ( "encoding/json" diff --git a/decision/prompt_builder_test.go b/kernel/prompt_builder_test.go similarity index 99% rename from decision/prompt_builder_test.go rename to kernel/prompt_builder_test.go index 1f1fdfc4..e36a2a73 100644 --- a/decision/prompt_builder_test.go +++ b/kernel/prompt_builder_test.go @@ -1,4 +1,4 @@ -package decision +package kernel import ( "strings" diff --git a/decision/schema.go b/kernel/schema.go similarity index 99% rename from decision/schema.go rename to kernel/schema.go index 5a4e0bda..c19cb165 100644 --- a/decision/schema.go +++ b/kernel/schema.go @@ -1,4 +1,4 @@ -package decision +package kernel import "fmt" diff --git a/decision/schema_test.go b/kernel/schema_test.go similarity index 99% rename from decision/schema_test.go rename to kernel/schema_test.go index 612b7976..1dead0ec 100644 --- a/decision/schema_test.go +++ b/kernel/schema_test.go @@ -1,4 +1,4 @@ -package decision +package kernel import ( "strings" diff --git a/decision/validate_test.go b/kernel/validate_test.go similarity index 99% rename from decision/validate_test.go rename to kernel/validate_test.go index a04f1642..6cff077d 100644 --- a/decision/validate_test.go +++ b/kernel/validate_test.go @@ -1,4 +1,4 @@ -package decision +package kernel import ( "testing" diff --git a/manager/trader_manager.go b/manager/trader_manager.go index c18c9a9d..650d9a4d 100644 --- a/manager/trader_manager.go +++ b/manager/trader_manager.go @@ -4,7 +4,7 @@ import ( "context" "fmt" "nofx/debate" - "nofx/decision" + "nofx/kernel" "nofx/logger" "nofx/store" "nofx/trader" @@ -19,7 +19,7 @@ type TraderExecutorAdapter struct { } // ExecuteDecision executes a trading decision -func (a *TraderExecutorAdapter) ExecuteDecision(d *decision.Decision) error { +func (a *TraderExecutorAdapter) ExecuteDecision(d *kernel.Decision) error { return a.autoTrader.ExecuteDecision(d) } diff --git a/trader/auto_trader.go b/trader/auto_trader.go index fcb025d7..51d2672e 100644 --- a/trader/auto_trader.go +++ b/trader/auto_trader.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" "math" - "nofx/decision" + "nofx/kernel" "nofx/experience" "nofx/logger" "nofx/market" @@ -104,7 +104,7 @@ type AutoTrader struct { trader Trader // Use Trader interface (supports multiple platforms) mcpClient mcp.AIClient store *store.Store // Data storage (decision records, etc.) - strategyEngine *decision.StrategyEngine // Strategy engine (uses strategy configuration) + strategyEngine *kernel.StrategyEngine // Strategy engine (uses strategy configuration) cycleNumber int // Current cycle number initialBalance float64 dailyPnL float64 @@ -310,7 +310,7 @@ func NewAutoTrader(config AutoTraderConfig, st *store.Store, userID string) (*Au if config.StrategyConfig == nil { return nil, fmt.Errorf("[%s] strategy not configured", config.Name) } - strategyEngine := decision.NewStrategyEngine(config.StrategyConfig) + strategyEngine := kernel.NewStrategyEngine(config.StrategyConfig) logger.Infof("✓ [%s] Using strategy engine (strategy configuration loaded)", config.Name) return &AutoTrader{ @@ -524,7 +524,7 @@ func (at *AutoTrader) runCycle() error { // 5. Use strategy engine to call AI for decision logger.Infof("🤖 Requesting AI analysis and decision... [Strategy Engine]") - aiDecision, err := decision.GetFullDecisionWithStrategy(ctx, at.mcpClient, at.strategyEngine, "balanced") + aiDecision, err := kernel.GetFullDecisionWithStrategy(ctx, at.mcpClient, at.strategyEngine, "balanced") if aiDecision != nil && aiDecision.AIRequestDurationMs > 0 { record.AIRequestDurationMs = aiDecision.AIRequestDurationMs @@ -585,8 +585,8 @@ func (at *AutoTrader) runCycle() error { // logger.Infof(strings.Repeat("-", 70) + "\n") // 7. Print AI decisions - // logger.Infof("📋 AI decision list (%d items):\n", len(decision.Decisions)) - // for i, d := range decision.Decisions { + // logger.Infof("📋 AI decision list (%d items):\n", len(kernel.Decisions)) + // for i, d := range kernel.Decisions { // logger.Infof(" [%d] %s: %s - %s", i+1, d.Symbol, d.Action, d.Reasoning) // if d.Action == "open_long" || d.Action == "open_short" { // logger.Infof(" Leverage: %dx | Position: %.2f USDT | Stop loss: %.4f | Take profit: %.4f", @@ -664,7 +664,7 @@ func (at *AutoTrader) runCycle() error { } // buildTradingContext builds trading context -func (at *AutoTrader) buildTradingContext() (*decision.Context, error) { +func (at *AutoTrader) buildTradingContext() (*kernel.Context, error) { // 1. Get account information balance, err := at.trader.GetBalance() if err != nil { @@ -701,7 +701,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) { return nil, fmt.Errorf("failed to get positions: %w", err) } - var positionInfos []decision.PositionInfo + var positionInfos []kernel.PositionInfo totalMarginUsed := 0.0 // Current position key set (for cleaning up closed position records) @@ -768,7 +768,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) { peakPnlPct := at.peakPnLCache[posKey] at.peakPnLCacheMutex.RUnlock() - positionInfos = append(positionInfos, decision.PositionInfo{ + positionInfos = append(positionInfos, kernel.PositionInfo{ Symbol: symbol, Side: side, EntryPrice: entryPrice, @@ -820,13 +820,13 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) { logger.Infof("📋 [%s] Strategy leverage config: BTC/ETH=%dx, Altcoin=%dx", at.name, btcEthLeverage, altcoinLeverage) // 6. Build context - ctx := &decision.Context{ + ctx := &kernel.Context{ CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"), RuntimeMinutes: int(time.Since(at.startTime).Minutes()), CallCount: at.callCount, BTCETHLeverage: btcEthLeverage, AltcoinLeverage: altcoinLeverage, - Account: decision.AccountInfo{ + Account: kernel.AccountInfo{ TotalEquity: totalEquity, AvailableBalance: availableBalance, UnrealizedPnL: totalUnrealizedProfit, @@ -859,7 +859,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) { exitTimeStr = time.Unix(trade.ExitTime, 0).UTC().Format("01-02 15:04 UTC") } - ctx.RecentOrders = append(ctx.RecentOrders, decision.RecentOrder{ + ctx.RecentOrders = append(ctx.RecentOrders, kernel.RecentOrder{ Symbol: trade.Symbol, Side: trade.Side, EntryPrice: trade.EntryPrice, @@ -881,7 +881,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) { } else if stats.TotalTrades == 0 { logger.Infof("⚠️ [%s] GetFullStats returned 0 trades (traderID=%s)", at.name, at.id) } else { - ctx.TradingStats = &decision.TradingStats{ + ctx.TradingStats = &kernel.TradingStats{ TotalTrades: stats.TotalTrades, WinRate: stats.WinRate, ProfitFactor: stats.ProfitFactor, @@ -933,7 +933,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) { } // executeDecisionWithRecord executes AI decision and records detailed information -func (at *AutoTrader) executeDecisionWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error { +func (at *AutoTrader) executeDecisionWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error { switch decision.Action { case "open_long": return at.executeOpenLongWithRecord(decision, actionRecord) @@ -953,7 +953,7 @@ func (at *AutoTrader) executeDecisionWithRecord(decision *decision.Decision, act // ExecuteDecision executes a trading decision from external sources (e.g., debate consensus) // This is a public method that can be called by other modules -func (at *AutoTrader) ExecuteDecision(d *decision.Decision) error { +func (at *AutoTrader) ExecuteDecision(d *kernel.Decision) error { logger.Infof("[%s] Executing external decision: %s %s", at.name, d.Action, d.Symbol) // Create a minimal action record for tracking @@ -979,7 +979,7 @@ func (at *AutoTrader) ExecuteDecision(d *decision.Decision) error { } // executeOpenLongWithRecord executes open long position and records detailed information -func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error { +func (at *AutoTrader) executeOpenLongWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error { logger.Infof(" 📈 Open long: %s", decision.Symbol) // ⚠️ Get current positions for multiple checks @@ -1096,7 +1096,7 @@ func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, act } // executeOpenShortWithRecord executes open short position and records detailed information -func (at *AutoTrader) executeOpenShortWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error { +func (at *AutoTrader) executeOpenShortWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error { logger.Infof(" 📉 Open short: %s", decision.Symbol) // ⚠️ Get current positions for multiple checks @@ -1213,7 +1213,7 @@ func (at *AutoTrader) executeOpenShortWithRecord(decision *decision.Decision, ac } // executeCloseLongWithRecord executes close long position and records detailed information -func (at *AutoTrader) executeCloseLongWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error { +func (at *AutoTrader) executeCloseLongWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error { logger.Infof(" 🔄 Close long: %s", decision.Symbol) // Get current price @@ -1277,7 +1277,7 @@ func (at *AutoTrader) executeCloseLongWithRecord(decision *decision.Decision, ac } // executeCloseShortWithRecord executes close short position and records detailed information -func (at *AutoTrader) executeCloseShortWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error { +func (at *AutoTrader) executeCloseShortWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error { logger.Infof(" 🔄 Close short: %s", decision.Symbol) // Get current price @@ -1392,7 +1392,7 @@ func (at *AutoTrader) GetSystemPromptTemplate() string { } // saveEquitySnapshot saves equity snapshot independently (for drawing profit curve, decoupled from AI decision) -func (at *AutoTrader) saveEquitySnapshot(ctx *decision.Context) { +func (at *AutoTrader) saveEquitySnapshot(ctx *kernel.Context) { if at.store == nil || ctx == nil { return } @@ -1624,7 +1624,7 @@ func calculatePnLPercentage(unrealizedPnl, marginUsed float64) float64 { // sortDecisionsByPriority sorts decisions: close positions first, then open positions, finally hold/wait // This avoids position stacking overflow when changing positions -func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision { +func sortDecisionsByPriority(decisions []kernel.Decision) []kernel.Decision { if len(decisions) <= 1 { return decisions } @@ -1644,7 +1644,7 @@ func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision } // Copy decision list - sorted := make([]decision.Decision, len(decisions)) + sorted := make([]kernel.Decision, len(decisions)) copy(sorted, decisions) // Sort by priority