refactor: rename decision package to kernel

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